diff --git a/.github/workflows/build-deps-windows.yml b/.github/workflows/build-deps-windows.yml new file mode 100644 index 0000000..c82dffe --- /dev/null +++ b/.github/workflows/build-deps-windows.yml @@ -0,0 +1,49 @@ +name: Build Windows Deps + +# Builds the VapourSynth/FFmpeg/Python dependency bundle for Windows x64. +# Mirrors build-deps-macos.yml / build-deps-linux.yml. Everything is fetched +# from upstream release URLs by download-deps-windows.ps1 (7-Zip is preinstalled +# on the windows-latest runner), so no local deps checkout is needed. + +on: + workflow_dispatch: + inputs: + version: + description: 'Deps version (e.g., 1.4.0)' + required: true + type: string + release_tag: + description: 'Upload to this release tag (e.g., deps-v1.4.0). Leave empty to skip upload.' + required: false + type: string + +permissions: + contents: write + +jobs: + build-x64: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Build dependencies + shell: pwsh + run: ./Scripts/download-deps-windows.ps1 + + - name: Package dependencies + shell: pwsh + run: ./Scripts/package-deps-windows.ps1 -Version "${{ inputs.version }}" + + - uses: actions/upload-artifact@v4 + with: + name: VapourBox-deps-${{ inputs.version }}-windows-x64 + path: dist/VapourBox-deps-${{ inputs.version }}-windows-x64.zip + + - name: Upload to release + if: inputs.release_tag != '' + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release upload "${{ inputs.release_tag }}" ` + "dist/VapourBox-deps-${{ inputs.version }}-windows-x64.zip" --clobber diff --git a/CLAUDE.md b/CLAUDE.md index 8e9c72d..472ab00 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -524,7 +524,7 @@ The `download-deps-windows.ps1` and `download-deps-macos.sh` scripts apply these - Fully self-contained deps (no Homebrew at runtime): Python 3.12 (python-build-standalone), VS built from source - Worker sets: `PYTHONHOME`, `PYTHONPATH`, `VAPOURSYNTH_CONF_PATH`, `DYLD_LIBRARY_PATH` - `vspipe` is a wrapper script that generates config dynamically (needed because `VAPOURSYNTH_PLUGIN_PATH` is additive, not a replacement) -- **x64 FFmpeg** is sourced pre-built from evermeet.cx (static x86_64, links only system frameworks); arm64 FFmpeg comes from Homebrew. **x64 plugins** build from source under Rosetta, except `tmedian`/`bestsource` which come pre-built from Stefan-Olt/vs-plugin-build. +- **FFmpeg** is sourced pre-built as a static binary that links only system frameworks: **x64** from evermeet.cx, **arm64** from martin-riedl.de (Homebrew's arm64 ffmpeg is dynamically linked to ~17 Homebrew dylibs and is NOT self-contained, so it can't be bundled). **x64 plugins** build from source under Rosetta, except `tmedian`/`bestsource` which come pre-built from Stefan-Olt/vs-plugin-build. - **Code signing**: After `install_name_tool` modifications, binaries must be re-signed: `codesign -s - -f ` (exit code 137 = SIGKILL means invalid signature) - Quarantine removal: `xattr -cr` on deps after download - Show in Folder: `open -R ` @@ -641,8 +641,9 @@ Create the app-specific password at appleid.apple.com → Sign-In and Security - Flutter Windows can't build on macOS — use GitHub Actions - Flutter Linux can't build on macOS — use GitHub Actions or a Linux VM -- Windows deps can be zipped on macOS if `deps/windows-x64/` exists -- Linux deps must be built on Linux (`./Scripts/download-deps-linux.sh`) +- Deps for every platform are built in CI by their own workflow: `build-deps-macos.yml` (arm64 + x64), `build-deps-linux.yml` (x64 + arm64), `build-deps-windows.yml` (x64, on `windows-latest` — everything is fetched from upstream release URLs by `download-deps-windows.ps1`, so no local checkout is needed). All take `version` + optional `release_tag` (leave empty to skip the release upload). +- Windows deps can also be zipped on macOS if `deps/windows-x64/` exists (`Scripts/package-deps-windows.ps1` via pwsh), but the CI workflow is the canonical path. +- Linux deps must be built on Linux (`./Scripts/download-deps-linux.sh`) or via `build-deps-linux.yml` - The macOS CI build (`build-macos.yml`) signs with the Developer ID cert and notarizes — see "macOS Code Signing & Notarization" below. Windows and Linux CI builds remain unsigned. - macOS ships as a **universal** (arm64+x86_64) app by default. `build-macos.yml` takes an `arch` input (`universal` | `both` | `arm64` | `x64`, default `universal`) and fans out via a matrix. Universal = lipo'd worker + Runner built with `ARCHS="arm64 x86_64"`; `both` = two separate single-arch DMGs. The x64 slice cross-compiles on the `macos-15` (arm64) runner. - Deps are **not** bundled — the app downloads `macos-arm64` or `macos-x64` deps at runtime per `uname`, so a universal app needs **both** deps bundles published. macOS deps are built by `build-deps-macos.yml` (arm64 on `macos-15`, x64 natively on `macos-15-intel`). diff --git a/Scripts/download-deps-linux.sh b/Scripts/download-deps-linux.sh index 30c9da5..c90a08c 100755 --- a/Scripts/download-deps-linux.sh +++ b/Scripts/download-deps-linux.sh @@ -617,6 +617,12 @@ build_plugin "dfttest" \ "libdfttest.so" \ "$PLUGIN_BUILD_ENV meson setup build --buildtype=release && ninja -C build" +# TTempSmooth (core.ttmpsm.TTempSmooth - used by havsfunc MCTemporalDenoise) +build_plugin "ttempsmooth" \ + "https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TTempSmooth.git" \ + "libttempsmooth.so" \ + "$PLUGIN_BUILD_ENV meson setup build --buildtype=release && ninja -C build" + # FFT3DFilter build_plugin "fft3dfilter" \ "https://github.com/myrsloik/VapourSynth-FFT3DFilter.git" \ diff --git a/Scripts/download-deps-macos.sh b/Scripts/download-deps-macos.sh index a75cc7c..c2595d2 100755 --- a/Scripts/download-deps-macos.sh +++ b/Scripts/download-deps-macos.sh @@ -93,7 +93,8 @@ BREW_DEPS=( # libdvdread -> libdvdread (DVD title extraction in the worker) # xz -> liblzma.5 (linked by Stefan-Olt's x86_64 bestsource) fftw boost libdvdread xz - # FFmpeg (arm64 copies this; x64 uses evermeet.cx static builds instead) + # FFmpeg (build-time convenience only; at runtime both arches ship static + # ffmpeg downloaded from evermeet.cx (x64) / martin-riedl.de (arm64)) ffmpeg ) @@ -346,10 +347,21 @@ if [ "$ARCH" = "x86_64" ]; then codesign -s - -f "$DEPS_DIR/ffmpeg/ffprobe" 2>/dev/null || true echo " Installed evermeet.cx FFmpeg" else - cp "$BREW_PREFIX/bin/ffmpeg" "$DEPS_DIR/ffmpeg/" - cp "$BREW_PREFIX/bin/ffprobe" "$DEPS_DIR/ffmpeg/" + # arm64: Homebrew's ffmpeg is dynamically linked against ~17 Homebrew dylibs + # (/opt/homebrew/Cellar/ffmpeg/.../lib*, x264, x265, dav1d, openssl, ...), so + # copying just the binary yields a bundle that won't run on users' Macs. + # Use the static arm64 build from martin-riedl.de (links only system + # frameworks, ~60 MB) - the same source the 1.3.0 deps shipped, and the + # arm64 analogue of the evermeet.cx static build used for x64 above. + echo " Downloading static arm64 FFmpeg from martin-riedl.de..." + curl -sL "https://ffmpeg.martin-riedl.de/redirect/latest/macos/arm64/release/ffmpeg.zip" -o "$BUILD_DIR/ffmpeg.zip" + curl -sL "https://ffmpeg.martin-riedl.de/redirect/latest/macos/arm64/release/ffprobe.zip" -o "$BUILD_DIR/ffprobe.zip" + unzip -q -o "$BUILD_DIR/ffmpeg.zip" -d "$DEPS_DIR/ffmpeg/" + unzip -q -o "$BUILD_DIR/ffprobe.zip" -d "$DEPS_DIR/ffmpeg/" chmod +x "$DEPS_DIR/ffmpeg/ffmpeg" "$DEPS_DIR/ffmpeg/ffprobe" - echo " Copied FFmpeg from Homebrew" + codesign -s - -f "$DEPS_DIR/ffmpeg/ffmpeg" 2>/dev/null || true + codesign -s - -f "$DEPS_DIR/ffmpeg/ffprobe" 2>/dev/null || true + echo " Installed static arm64 FFmpeg from martin-riedl.de" fi # ============================================================================ @@ -401,6 +413,17 @@ if [ "$ARCH" = "arm64" ]; then install_name_tool -change "@rpath/libfftw3f_threads.3.dylib" "@loader_path/../../lib/libfftw3f_threads.3.dylib" "$PLUGINS_DIR/libdfttest.dylib" codesign -s - -f "$PLUGINS_DIR/libdfttest.dylib" 2>/dev/null + # TTempSmooth (core.ttmpsm.TTempSmooth - used by havsfunc MCTemporalDenoise) + curl -sL "$YUYGFGG_BASE/lib/libttempsmooth.dylib" -o "$PLUGINS_DIR/libttempsmooth.dylib" + install_name_tool -id "@loader_path/libttempsmooth.dylib" "$PLUGINS_DIR/libttempsmooth.dylib" 2>/dev/null || true + codesign -s - -f "$PLUGINS_DIR/libttempsmooth.dylib" 2>/dev/null + + # FFT3DFilter (also used by MCTemporalDenoise; links FFTW like dfttest) + curl -sL "$YUYGFGG_BASE/lib/libfft3dfilter.dylib" -o "$PLUGINS_DIR/libfft3dfilter.dylib" + install_name_tool -change "@rpath/libfftw3f.3.dylib" "@loader_path/../../lib/libfftw3f.3.dylib" "$PLUGINS_DIR/libfft3dfilter.dylib" 2>/dev/null || true + install_name_tool -change "@rpath/libfftw3f_threads.3.dylib" "@loader_path/../../lib/libfftw3f_threads.3.dylib" "$PLUGINS_DIR/libfft3dfilter.dylib" 2>/dev/null || true + codesign -s - -f "$PLUGINS_DIR/libfft3dfilter.dylib" 2>/dev/null + # VIVTC (inverse telecine - VFM + VDecimate) curl -sL "$YUYGFGG_BASE/lib/libvivtc.dylib" -o "$PLUGINS_DIR/libvivtc.dylib" install_name_tool -id "@loader_path/libvivtc.dylib" "$PLUGINS_DIR/libvivtc.dylib" @@ -475,7 +498,27 @@ build_plugin() { cd "$name" - if eval "$build_cmd"; then + # Some plugins' meson.build locates the VapourSynth headers by running + # `import vapoursynth as vs; print(vs.get_include())` in the host python + # (e.g. mvtools, bm3d, eedi3m). On a clean build machine the host python has + # no `vapoursynth` module (the runtime VS module is built against our + # embedded python, not the host's), so that probe fails. Replace it with the + # from-source VS include dir - same trick already used for vivtc below. + if [ -n "$VS_INC_DIR" ] && [ -f meson.build ] && grep -q "import vapoursynth" meson.build; then + VS_INC_DIR="$VS_INC_DIR" python3 - meson.build <<'PYEOF' +import os, re, sys +path = sys.argv[1] +inc = os.environ["VS_INC_DIR"] +s = open(path).read() +s = re.sub(r"run_command\(.*?\)\.stdout\(\)\.strip\(\)", repr(inc), s, flags=re.S) +open(path, "w").write(s) +PYEOF + fi + + # Expose the from-source VapourSynth to pkg-config so plugins that use + # `dependency('vapoursynth')` (removegrain, cas, ...) resolve against our + # R73 build rather than failing or finding a mismatched system install. + if PKG_CONFIG_PATH="${VS_PC_DIR:-}:${PKG_CONFIG_PATH:-}" eval "$build_cmd"; then # Find the built library local lib_path=$(find . -name "*.dylib" -type f 2>/dev/null | head -1) if [ -n "$lib_path" ]; then @@ -526,6 +569,13 @@ download_prebuilt_plugin() { STEFANOLT="https://github.com/Stefan-Olt/vs-plugin-build/releases/download/vsplugin" +# pkg-config dir + header dir of the from-source VapourSynth install. Used by the +# plugin builds on BOTH arches (see build_plugin and the x86_64 source builds): +# the host python has no `vapoursynth` module on a clean runner, so meson plugins +# can't probe it the usual way and must be pointed at these explicitly. +VS_PC_DIR="$VS_INSTALL_DIR/lib/pkgconfig" +VS_INC_DIR="$(dirname "$(find "$VS_INSTALL_DIR/include" -name 'VapourSynth4.h' 2>/dev/null | head -1)")" + if [ "$ARCH" = "x86_64" ]; then # ======================================================================== # x86_64 plugins: download pre-built darwin-x86_64 binaries from @@ -570,8 +620,6 @@ if [ "$ARCH" = "x86_64" ]; then # ---- The four plugins Stefan-Olt does not ship: build from source ---- cd "$BUILD_DIR" - VS_PC_DIR="$VS_INSTALL_DIR/lib/pkgconfig" - VS_INC_DIR="$(dirname "$(find "$VS_INSTALL_DIR/include" -name 'VapourSynth4.h' 2>/dev/null | head -1)")" # neo-f3kdb (cmake; uses its bundled VapourSynth headers, VCL2 submodule) if [ "$FORCE" = true ] || [ ! -f "$PLUGINS_DIR/libneo-f3kdb.dylib" ]; then @@ -657,6 +705,53 @@ PYEOF fi cd "$BUILD_DIR" fi + + # ttmpsm (TTempSmooth - meson; finds VapourSynth via pkg-config, no extra deps) + # Used by havsfunc MCTemporalDenoise. + if [ "$FORCE" = true ] || [ ! -f "$PLUGINS_DIR/libttempsmooth.dylib" ]; then + echo ""; echo "=== Building TTempSmooth (x86_64) ===" + rm -rf ttempsmooth + if git clone --depth 1 https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TTempSmooth.git ttempsmooth \ + && PKG_CONFIG_PATH="$VS_PC_DIR:${PKG_CONFIG_PATH:-}" \ + meson setup ttempsmooth/build ttempsmooth --buildtype=release \ + && ninja -C ttempsmooth/build; then + lib=$(find ttempsmooth/build -name "*.dylib" -type f | head -1) + if [ -n "$lib" ]; then + cp "$lib" "$PLUGINS_DIR/libttempsmooth.dylib" + install_name_tool -id "@loader_path/libttempsmooth.dylib" "$PLUGINS_DIR/libttempsmooth.dylib" 2>/dev/null || true + codesign -s - -f "$PLUGINS_DIR/libttempsmooth.dylib" 2>/dev/null || true + echo " Built TTempSmooth" + else echo " no dylib"; FAILED_PLUGINS+=("ttempsmooth"); fi + else + echo " Failed to build TTempSmooth"; FAILED_PLUGINS+=("ttempsmooth") + fi + cd "$BUILD_DIR" + fi + + # fft3dfilter (meson; links Homebrew FFTW, repoint at the bundled lib/ copy). + # Also used by havsfunc MCTemporalDenoise. + if [ "$FORCE" = true ] || [ ! -f "$PLUGINS_DIR/libfft3dfilter.dylib" ]; then + echo ""; echo "=== Building FFT3DFilter (x86_64) ===" + rm -rf fft3dfilter + if git clone --depth 1 https://github.com/myrsloik/VapourSynth-FFT3DFilter.git fft3dfilter \ + && PKG_CONFIG_PATH="$VS_PC_DIR:$BREW_PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}" \ + meson setup fft3dfilter/build fft3dfilter --buildtype=release \ + && ninja -C fft3dfilter/build; then + lib=$(find fft3dfilter/build -name "*.dylib" -type f | head -1) + if [ -n "$lib" ]; then + cp "$lib" "$PLUGINS_DIR/libfft3dfilter.dylib" + install_name_tool -change "$BREW_PREFIX/opt/fftw/lib/libfftw3f.3.dylib" \ + "@loader_path/../../lib/libfftw3f.3.dylib" "$PLUGINS_DIR/libfft3dfilter.dylib" 2>/dev/null || true + install_name_tool -change "$BREW_PREFIX/opt/fftw/lib/libfftw3f_threads.3.dylib" \ + "@loader_path/../../lib/libfftw3f_threads.3.dylib" "$PLUGINS_DIR/libfft3dfilter.dylib" 2>/dev/null || true + codesign -s - -f "$PLUGINS_DIR/libfft3dfilter.dylib" 2>/dev/null || true + echo " Built FFT3DFilter" + else echo " no dylib"; FAILED_PLUGINS+=("fft3dfilter"); fi + else + echo " Failed to build FFT3DFilter"; FAILED_PLUGINS+=("fft3dfilter") + fi + cd "$BUILD_DIR" + fi else # MVTools (essential for QTGMC motion compensation) build_plugin "mvtools" \ @@ -669,7 +764,10 @@ echo "" echo "=== Building ZNEDI3 ===" if [ "$FORCE" = true ] || [ ! -f "$PLUGINS_DIR/libznedi3.dylib" ]; then rm -rf znedi3 - git clone --depth 1 https://github.com/sekrit-twc/znedi3.git znedi3 + # --recurse-submodules: znedi3 carries graphengine + vsxx (which bundles the + # VapourSynth headers) as submodules. Without them the build fails with + # "'znedi3.h' file not found" / missing vsxx4_pluginmain.o. + git clone --depth 1 --recurse-submodules --shallow-submodules https://github.com/sekrit-twc/znedi3.git znedi3 cd znedi3 # ZNEDI3 has its own makefile - need to disable x86 optimizations on arm64 if [ "$ARCH" = "arm64" ]; then @@ -694,10 +792,13 @@ else fi # NNEDI3 (CPU version) +# Its Makefile.am hardcodes `-mfpu=neon` for the NEON path, which clang rejects +# on arm64 ('unsupported option -mfpu='). NEON is baseline on aarch64, so the +# flag isn't needed - strip it before autogen regenerates the Makefiles. build_plugin "nnedi3" \ "https://github.com/dubhater/vapoursynth-nnedi3.git" \ "libnnedi3.dylib" \ - "./autogen.sh && ./configure && make -j\$(sysctl -n hw.ncpu) && cp .libs/libnnedi3.dylib . 2>/dev/null || cp src/.libs/libnnedi3.dylib . 2>/dev/null" + "sed -i '' 's/ -mfpu=neon//' Makefile.am && ./autogen.sh && ./configure && make -j\$(sysctl -n hw.ncpu)" # NNEDI3CL (OpenCL version) build_plugin "nnedi3cl" \ @@ -741,10 +842,16 @@ else fi # FFT3DFilter -build_plugin "fft3dfilter" \ - "https://github.com/myrsloik/VapourSynth-FFT3DFilter.git" \ - "libfft3dfilter.dylib" \ - "meson setup build --buildtype=release && ninja -C build" +# On ARM64 we use the pre-built version from yuygfgg (downloaded above); its +# from-source build needs fftw3f_threads which isn't reliably discoverable here. +if [ "$ARCH" != "arm64" ]; then + build_plugin "fft3dfilter" \ + "https://github.com/myrsloik/VapourSynth-FFT3DFilter.git" \ + "libfft3dfilter.dylib" \ + "meson setup build --buildtype=release && ninja -C build" +else + echo " FFT3DFilter: using pre-built ARM64 version from yuygfgg" +fi # MiscFilters build_plugin "miscfilters" \ @@ -1083,6 +1190,40 @@ else: EOF fi +# ============================================================================ +# Repoint plugin support-lib references at the bundled copies in lib/ +# ============================================================================ +# Source-built plugins (mvtools, bm3d, dctfilter, nnedi3cl, ...) link Homebrew's +# fftw/boost by absolute path (e.g. /opt/homebrew/opt/fftw/lib/libfftw3f.3.dylib). +# Those paths don't exist on users' machines, so the plugin would fail to load. +# The libs are already bundled in $LIB_DIR; rewrite every such reference at the +# bundled copy. Also normalize each plugin's own install id to @loader_path so +# the bundle is fully relocatable. +echo "" +echo "=== Repointing plugin support-lib references to bundled lib/ ===" +SUPPORT_LIBS=(libfftw3f.3.dylib libfftw3f_threads.3.dylib libboost_filesystem.dylib libboost_atomic.dylib liblzma.5.dylib libdvdread.dylib) +for plugin in "$PLUGINS_DIR"/*.dylib; do + [ -f "$plugin" ] || continue + plugin_base=$(basename "$plugin") + changed=false + # Normalize the plugin's own install id. + install_name_tool -id "@loader_path/$plugin_base" "$plugin" 2>/dev/null && changed=true + # Repoint any dependency that matches a bundled support lib by basename. + while IFS= read -r ref; do + ref_base=$(basename "$ref") + case "$ref_base" in "$plugin_base") continue ;; esac + for sl in "${SUPPORT_LIBS[@]}"; do + if [ "$ref_base" = "$sl" ] && [ -f "$LIB_DIR/$sl" ] && [[ "$ref" != @loader_path/* ]]; then + install_name_tool -change "$ref" "@loader_path/../../lib/$sl" "$plugin" 2>/dev/null && changed=true + fi + done + done < <(otool -L "$plugin" | tail -n +2 | awk '{print $1}') + if [ "$changed" = true ]; then + codesign -s - -f "$plugin" 2>/dev/null || true + echo " Repointed $plugin_base" + fi +done + # ============================================================================ # Sign all binaries and libraries (required for macOS code signing) # ============================================================================ diff --git a/Scripts/download-deps-windows.ps1 b/Scripts/download-deps-windows.ps1 index f6763c7..2b7dc33 100644 --- a/Scripts/download-deps-windows.ps1 +++ b/Scripts/download-deps-windows.ps1 @@ -207,6 +207,12 @@ $Plugins7z = @( Url = "https://github.com/HomeOfVapourSynthEvolution/VapourSynth-DFTTest/releases/download/r7/DFTTest-r7.7z" Check = "DFTTest.dll" }, + @{ + # core.ttmpsm.TTempSmooth - used by havsfunc MCTemporalDenoise + Name = "ttempsmooth" + Url = "https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TTempSmooth/releases/download/r4.1/TTempSmooth-r4.1-win64.7z" + Check = "TTempSmooth.dll" + }, @{ Name = "neo_f3kdb" Url = "https://github.com/HomeOfAviSynthPlusEvolution/neo_f3kdb/releases/download/r10/neo_f3kdb_r10.7z" diff --git a/app/assets/deps-version.json b/app/assets/deps-version.json index 8889e19..6324e1e 100644 --- a/app/assets/deps-version.json +++ b/app/assets/deps-version.json @@ -1,36 +1,32 @@ { - "version": "1.3.0", - "releaseTag": "deps-v1.3.0", - "releaseDate": "2026-04-07", + "version": "1.4.0", + "releaseTag": "deps-v1.4.0", + "releaseDate": "2026-06-09", "platforms": { "windows-x64": { - "filename": "VapourBox-deps-1.3.0-windows-x64.zip", - "sha256": "b404bc4434b47116dc8acb152c139327c7d59fa9eee4e30a8d8b1a1f09b0799d", - "size": 172721085 + "filename": "VapourBox-deps-1.4.0-windows-x64.zip", + "sha256": null, + "size": null }, "macos-arm64": { - "filename": "VapourBox-deps-1.3.0-macos-arm64.zip", - "sha256": "959ce05ad67449ca2b680add0092a3a117c9a3f78e3f474e80d6bc49d6a110d3", - "size": 98931909 + "filename": "VapourBox-deps-1.4.0-macos-arm64.zip", + "sha256": null, + "size": null }, "macos-x64": { - "filename": "VapourBox-deps-1.3.0-macos-x64.zip", - "sha256": "a326b662e34bb737269f6792b4b1b8518d8512bb05046cf430632a0213202abf", - "size": 106078101 + "filename": "VapourBox-deps-1.4.0-macos-x64.zip", + "sha256": null, + "size": null }, "linux-x64": { - "version": "1.4.0", - "releaseTag": "deps-v1.4.0", "filename": "VapourBox-deps-1.4.0-linux-x64.zip", - "sha256": "2c3e7cf458d5443fe827242e5bd8ce12a47db57aa19f1d51af248ad80f1a519c", - "size": 191028656 + "sha256": null, + "size": null }, "linux-arm64": { - "version": "1.4.0", - "releaseTag": "deps-v1.4.0", "filename": "VapourBox-deps-1.4.0-linux-arm64.zip", - "sha256": "45986e0d70acd0b93a59aecfd6a493716ccfe963608194e1cfaabc948680fbd8", - "size": 169664443 + "sha256": null, + "size": null } }, "githubRepo": "StuartCameronCode/VapourBox" diff --git a/docs/macos-arm64-deps-build-handoff.md b/docs/macos-arm64-deps-build-handoff.md new file mode 100644 index 0000000..3e297cd --- /dev/null +++ b/docs/macos-arm64-deps-build-handoff.md @@ -0,0 +1,99 @@ +# Handoff: Fix the macOS arm64 dependency build + +**For:** a Claude Code instance running on a **macOS (Apple Silicon)** machine — you can build the deps locally, which the Linux dev box could not. + +**Goal:** get `build-deps-macos.yml` (and `./Scripts/download-deps-macos.sh`) producing a complete **macOS arm64** dependency bundle again, with **all** plugins built — including the newly added `ttmpsm` (TTempSmooth) and `fft3dfilter`, which `havsfunc.MCTemporalDenoise` needs. + +--- + +## Background / why this exists + +A user hit a crash: `No attribute with the name ttmpsm` — `MCTemporalDenoise` calls `core.ttmpsm.TTempSmooth` (and `core.fft3dfilter.FFT3DFilter`), which were missing from the shipped deps. We verified by inspecting the published deps zips: + +- `ttmpsm` was missing on **all** platforms. +- macOS additionally lacked **`fft3dfilter`**. + +Fixes were added on branch **`fix-ttempsmooth-plugin`** (PR pending): +- **Linux** — `ttmpsm` via `build_plugin` in `download-deps-linux.sh`. ✅ Verified building in CI. +- **Windows** — `ttmpsm` prebuilt (`TTempSmooth-r4.1-win64.7z`) in `download-deps-windows.ps1`. `fft3dfilter` already present. +- **macOS** — `ttmpsm` + `fft3dfilter` added to `download-deps-macos.sh`: + - **arm64**: downloaded as prebuilt dylibs from the `yuygfgg/Macos_vapoursynth_plugins` pool (mirrors the existing `dfttest` handling, with the FFTW `install_name_tool` repoint). + - **x86_64**: built from source via meson (mirrors `descratch`/`nnedi3cl`). ✅ **macOS x64 CI build succeeded** — `Built TTempSmooth` and `Built FFT3DFilter`. + +deps version was unified to **1.4.0** (`deps-v1.4.0`) across all platforms (`app/assets/deps-version.json`, sha256/size currently `null` pending a clean rebuild). + +## The actual problem to fix + +The **macОS arm64** job of `build-deps-macos.yml` **fails**, but **not** because of the TTempSmooth/FFT3DFilter additions — those run earlier (prebuilt downloads) and aren't implicated. It fails on **pre-existing** plugin builds, due to **runner environment drift**: + +- The `macos-15` runner now ships **Homebrew Python 3.14**. +- `mvtools` (meson) fails: + ``` + meson.build:14:4: ERROR: Command + `/opt/homebrew/opt/python@3.14/bin/python3.14 -c 'import vapoursynth as vs; print(vs.get_include())'` + failed with status 1. + ModuleNotFoundError: No module named 'vapoursynth' + ``` + i.e. the plugin's meson uses a runtime `import vapoursynth` to locate VS headers, and the runner's python has no `vapoursynth` module. +- `znedi3` (make) fails: `vsznedi3/vsznedi3.cpp:7:10: fatal error: 'znedi3.h' file not found`. + +This is **not** caused by our change — recent `main` builds of `build-deps-macos.yml` were **also failing** (2026-06-08 had multiple failures). It's environmental and predates the branch. + +### Evidence / references +- Failing run (branch `fix-ttempsmooth-plugin`): `build-deps-macos.yml` run **27208818617** — `build-x64` ✅, `build-arm64` ❌. +- The matching **Linux** run **27208815698** is fully green (so the cross-platform plugin work itself is sound). +- `gh run list --workflow build-deps-macos.yml --limit 8` shows pre-branch failures on `main`. + +## Where things live +- Script: `Scripts/download-deps-macos.sh` + - `build_plugin()` helper (clones + `eval "$build_cmd"`, copies first `*.dylib`). + - arm64 prebuilt block (yuygfgg) — where `dfttest`/`ttmpsm`/`fft3dfilter` are pulled. + - x86_64 source-build block — `neo-f3kdb`, `nnedi3cl`, `vivtc`, `descratch`, and now `ttmpsm`/`fft3dfilter`. + - **Template for the fix already in the script:** the `vivtc` build patches its `meson.build` to replace the `run_command(python … vs.get_include())` probe with the VS headers path (search for `VS_INC_DIR` / the inline `python3 - vivtc/meson.build <<'PYEOF'` heredoc). `mvtools`/`znedi3` need the equivalent. +- Workflow: `.github/workflows/build-deps-macos.yml` (arm64 native on `macos-15`; `workflow_dispatch` with `version`, `release_tag`, `arch`). + +## Suggested investigation / fix steps + +1. **Reproduce locally** on the Apple Silicon machine: + ```bash + ./Scripts/download-deps-macos.sh --force + ``` + Watch for the `mvtools` and `znedi3` failures (and note any others — the build is flaky; `nnedi3`/`bm3d` have also failed in CI). + +2. **mvtools (and any meson plugin using the python VS-include probe):** make the build locate VapourSynth headers without `import vapoursynth`. Options, in order of preference: + - Pass the built VS via pkg-config: set `PKG_CONFIG_PATH` to the VS install's `lib/pkgconfig` for the `build_plugin` meson invocations, if the plugin's `meson.build` prefers `dependency('vapoursynth')`. + - Otherwise, **patch the plugin's `meson.build`** to hardcode the VS include dir — reuse the exact approach already used for `vivtc` in this script. Consider extending `build_plugin()` to apply this patch generically. + +3. **znedi3 (`znedi3.h` not found):** ensure the build clones submodules / sees its headers. Check the znedi3 build invocation in the script (it's a `make`-based build); the header is part of the repo/submodule — confirm `--recurse-submodules` and the include path. + +4. **Re-run `./Scripts/download-deps-macos.sh --force` until it completes with an empty/expected `FAILED_PLUGINS` list.** Important: `build_plugin` failures are **non-fatal** (the job can report "success" while plugins silently fail), so check the **`Failed plugins:` summary line**, not just the exit code. + +5. **Verify the new plugins actually load** (and the reported bug is fixed) on arm64: + ```python + import vapoursynth as vs + c = vs.core + print(c.ttmpsm.TTempSmooth) + print(c.fft3dfilter.FFT3DFilter) + import havsfunc as haf + clip = c.std.BlankClip(width=160, height=120, format=vs.YUV422P8, length=20, fpsnum=25, fpsden=1) + haf.MCTemporalDenoise(clip, settings='medium').get_frame(5) # must not raise + ``` + (Run with the bundled-deps environment: `PYTHONHOME`/`PYTHONPATH`/`VAPOURSYNTH_PLUGIN_PATH`/`DYLD_LIBRARY_PATH`/`VAPOURSYNTH_CONF_PATH` per `worker/src/dependency_locator.rs` macOS branch.) + +6. **Confirm the FFTW dylib path resolves at runtime for `fft3dfilter`** (the arm64 yuygfgg dylib and the x64 source build are repointed at `@loader_path/../../lib/libfftw3f.3.dylib`). The CI build only proves it *compiles*; loading `core.fft3dfilter` is the real check — do it in step 5. + +7. **Run CI** to confirm: + ```bash + gh workflow run build-deps-macos.yml --ref fix-ttempsmooth-plugin -f version=1.4.0 -f arch=both + ``` + Both `build-arm64` and `build-x64` should be green and ship all plugins. + +## Definition of done +- `build-deps-macos.yml` green for **both** arm64 and x64 on `fix-ttempsmooth-plugin`, with no unexpected `FAILED_PLUGINS`. +- `core.ttmpsm` and `core.fft3dfilter` load on macOS arm64 and `MCTemporalDenoise` runs. +- macОS deps published at `deps-v1.4.0`; fill `app/assets/deps-version.json` → `macos-arm64`/`macos-x64` `sha256`+`size`. + +## Notes / gotchas +- Don't re-add TTempSmooth/FFT3DFilter — they're already in the script; the work is unblocking the **pre-existing** `mvtools`/`znedi3` (and possibly `nnedi3`/`bm3d`) builds on the current runner image. +- The same Python-3.14 `import vapoursynth` probe issue may eventually hit other meson plugins — a generic fix in `build_plugin()` is preferable to per-plugin patches. +- macОS deps build is **flaky** in general; treat a single green run as necessary-but-confirm by checking the `Failed plugins:` line.