Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/build-deps-windows.yml
Original file line number Diff line number Diff line change
@@ -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
7 changes: 4 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <binary>` (exit code 137 = SIGKILL means invalid signature)
- Quarantine removal: `xattr -cr` on deps after download
- Show in Folder: `open -R <path>`
Expand Down Expand Up @@ -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`).
Expand Down
6 changes: 6 additions & 0 deletions Scripts/download-deps-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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" \
Expand Down
167 changes: 154 additions & 13 deletions Scripts/download-deps-macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down Expand Up @@ -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

# ============================================================================
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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" \
Expand All @@ -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
Expand All @@ -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" \
Expand Down Expand Up @@ -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" \
Expand Down Expand Up @@ -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)
# ============================================================================
Expand Down
6 changes: 6 additions & 0 deletions Scripts/download-deps-windows.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading
Loading