From 00a0fa5a297df310137bd033b764fe0945a7f0fc Mon Sep 17 00:00:00 2001 From: Max Boksem <15022344+MaxMB15@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:35:42 +0200 Subject: [PATCH 1/8] fix: use ao=auto instead of hardcoded audio output list on Linux Hardcoding "pipewire,pulse,alsa" prevented mpv from trying other compiled-in audio backends (sdl, oss, jack, sndio). Using ao=auto lets mpv try all available outputs, improving compatibility with different libmpv builds. Co-Authored-By: Claude Opus 4.6 --- crates/tauri-plugin-mpv/src/linux.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/tauri-plugin-mpv/src/linux.rs b/crates/tauri-plugin-mpv/src/linux.rs index fa79e14..5c76e55 100644 --- a/crates/tauri-plugin-mpv/src/linux.rs +++ b/crates/tauri-plugin-mpv/src/linux.rs @@ -1549,7 +1549,10 @@ pub fn embedded_options() -> Vec<(&'static str, &'static str)> { // color corruption (red/green hue) on drivers that can't handle the // NV12→RGB interop (VMware, some Mesa/Intel setups). ("hwdec", "auto-copy"), - ("ao", "pipewire,pulse,alsa"), + // Don't restrict audio outputs — let mpv try ALL compiled-in backends. + // Hardcoding "pipewire,pulse,alsa" prevented fallback to other outputs + // (sdl, oss, jack, sndio) on systems where libmpv was built differently. + ("ao", "auto"), // audio sync is correct for callback-driven rendering (not vsync-locked). // display-resample requires calling render() at every vsync, which our // idle_add_once approach doesn't guarantee — it deactivates after a few seconds. @@ -1569,7 +1572,7 @@ pub fn embedded_options() -> Vec<(&'static str, &'static str)> { pub fn fallback_options() -> Vec<(&'static str, &'static str)> { vec![ ("hwdec", "auto"), - ("ao", "pipewire,pulse,alsa"), + ("ao", "auto"), ("video-sync", "display-resample"), ("cache", "yes"), ("demuxer-max-bytes", "150MiB"), @@ -1587,7 +1590,7 @@ pub fn software_fallback_options() -> Vec<(&'static str, &'static str)> { vec![ ("vo", "x11"), ("hwdec", "no"), - ("ao", "pipewire,pulse,alsa"), + ("ao", "auto"), ("cache", "yes"), ("demuxer-max-bytes", "150MiB"), ("demuxer-max-back-bytes", "75MiB"), From f02c8ec2e2bd401d3eb283c92599ab17236d9787 Mon Sep 17 00:00:00 2001 From: Max Boksem <15022344+MaxMB15@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:59:27 +0200 Subject: [PATCH 2/8] feat: build and bundle libmpv from source on Linux with full audio support Build libmpv from source in CI instead of using system libmpv-dev, ensuring ALSA + PulseAudio audio outputs are always compiled in. This prevents the "no audio output found" issue on systems with stripped libmpv packages. - build-libmpv.sh: explicitly enable alsa/pulse/pipewire audio when dev headers available - bundle-libmpv-linux.sh: prefer source build, recursively bundle transitive deps, set RPATH on all bundled .so files, verify audio libs are present - build.rs: prefer libs/linux/ (source build) over system pkg-config - CI (build.yml, release.yml): install audio/ffmpeg dev packages, build from source - tauri.conf.json: add audio (libpulse0|libasound2) and subtitle (libass) deps for deb/rpm - package_update: use apt-get install (resolves deps) instead of dpkg -i for deb updates Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build.yml | 12 +++- .github/workflows/release.yml | 13 ++++- apps/desktop/src-tauri/src/commands.rs | 32 ++++++++--- apps/desktop/src-tauri/tauri.conf.json | 18 +++++- crates/tauri-plugin-mpv/build.rs | 28 +++++---- scripts/build-libmpv.sh | 52 ++++++++++++++--- scripts/bundle-libmpv-linux.sh | 80 ++++++++++++++++++++------ 7 files changed, 183 insertions(+), 52 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 98759a8..70e81da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -255,11 +255,19 @@ jobs: run: | sudo apt-get update sudo apt-get install -y \ - libmpv-dev libgtk-3-dev libwebkit2gtk-4.1-dev \ + libgtk-3-dev libwebkit2gtk-4.1-dev \ libjavascriptcoregtk-4.1-dev libsoup-3.0-dev \ libayatana-appindicator3-dev \ libegl-dev libssl-dev pkg-config librsvg2-dev \ - patchelf rpm + patchelf rpm \ + meson ninja-build \ + libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev \ + libass-dev libpulse-dev libasound2-dev + + - name: Build libmpv from source + run: | + ./scripts/build-libmpv.sh linux + echo "LD_LIBRARY_PATH=$(pwd)/libs/linux:${LD_LIBRARY_PATH:-}" >> "$GITHUB_ENV" - name: Install frontend deps run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0188dd6..a997c1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -228,11 +228,20 @@ jobs: run: | sudo apt-get update sudo apt-get install -y \ - libmpv-dev libgtk-3-dev libwebkit2gtk-4.1-dev \ + libgtk-3-dev libwebkit2gtk-4.1-dev \ libjavascriptcoregtk-4.1-dev libsoup-3.0-dev \ libayatana-appindicator3-dev \ libegl-dev libssl-dev pkg-config librsvg2-dev \ - patchelf rpm + patchelf rpm \ + meson ninja-build \ + libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev \ + libass-dev libpulse-dev libasound2-dev + + - name: Build libmpv from source + run: | + ./scripts/build-libmpv.sh linux + echo "LD_LIBRARY_PATH=$(pwd)/libs/linux:${LD_LIBRARY_PATH:-}" >> "$GITHUB_ENV" + echo "PKG_CONFIG_PATH=$(pwd)/libs/linux/pkgconfig:${PKG_CONFIG_PATH:-}" >> "$GITHUB_ENV" - name: Prepare signing key run: | diff --git a/apps/desktop/src-tauri/src/commands.rs b/apps/desktop/src-tauri/src/commands.rs index 0d9950e..2959f1d 100644 --- a/apps/desktop/src-tauri/src/commands.rs +++ b/apps/desktop/src-tauri/src/commands.rs @@ -1270,14 +1270,32 @@ pub async fn package_update(app: AppHandle) -> Result<(), String> drop(file); // 3. Install via pkexec (async so we do not block the Tokio worker for the whole GUI prompt) + // For deb: prefer apt-get install which resolves dependencies automatically, + // fall back to dpkg -i for systems without apt-get. let output = match info.install_type.as_str() { - "deb" => tokio::process::Command::new("pkexec") - .arg("dpkg") - .arg("-i") - .arg(&tmp_path) - .output() - .await - .map_err(|e| format!("Failed to run installer: {e}"))?, + "deb" => { + let apt_result = tokio::process::Command::new("pkexec") + .arg("apt-get") + .arg("install") + .arg("-y") + .arg("--allow-downgrades") + .arg(&tmp_path) + .output() + .await; + match apt_result { + Ok(o) if o.status.success() => o, + _ => { + // Fallback to dpkg -i (no dependency resolution) + tokio::process::Command::new("pkexec") + .arg("dpkg") + .arg("-i") + .arg(&tmp_path) + .output() + .await + .map_err(|e| format!("Failed to run installer: {e}"))? + } + } + }, "rpm" => tokio::process::Command::new("pkexec") .arg("rpm") .arg("-U") diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 3e75709..cabc141 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -63,10 +63,24 @@ }, "linux": { "deb": { - "depends": ["libmpv2 | libmpv1", "libegl1", "libgtk-3-0", "libwebkit2gtk-4.1-0"] + "depends": [ + "libmpv2 | libmpv1", + "libegl1", + "libgtk-3-0", + "libwebkit2gtk-4.1-0", + "libpulse0 | libasound2", + "libass9" + ] }, "rpm": { - "depends": ["mpv-libs", "mesa-libEGL", "gtk3", "webkit2gtk4.1"] + "depends": [ + "mpv-libs", + "mesa-libEGL", + "gtk3", + "webkit2gtk4.1", + "pulseaudio-libs", + "libass" + ] } } } diff --git a/crates/tauri-plugin-mpv/build.rs b/crates/tauri-plugin-mpv/build.rs index 7d263c4..e8cb956 100644 --- a/crates/tauri-plugin-mpv/build.rs +++ b/crates/tauri-plugin-mpv/build.rs @@ -61,7 +61,22 @@ fn link_libmpv() { #[cfg(target_os = "linux")] { - // Use pkg-config if available; libmpv2-sys may handle this, but we ensure the link + // Prefer libs/linux/ (source build with audio support) over system pkg-config + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let libs_linux = std::path::Path::new(&manifest_dir) + .join("..") + .join("..") + .join("libs") + .join("linux"); + if libs_linux.join("libmpv.so").exists() { + if let Ok(abs) = libs_linux.canonicalize() { + println!("cargo:rustc-link-search=native={}", abs.display()); + } else { + println!("cargo:rustc-link-search=native={}", libs_linux.display()); + } + return; + } + // Fallback: system pkg-config (for development with libmpv-dev) if let Ok(output) = std::process::Command::new("pkg-config") .args(["--libs", "--cflags", "libmpv"]) .output() @@ -76,17 +91,6 @@ fn link_libmpv() { return; } } - // Fallback: libs/linux/ at workspace root - let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - let libs_linux = std::path::Path::new(&manifest_dir) - .join("..") - .join("..") - .join("libs") - .join("linux"); - if libs_linux.join("libmpv.so").exists() { - println!("cargo:rustc-link-search=native={}", libs_linux.display()); - return; - } } #[cfg(target_os = "windows")] diff --git a/scripts/build-libmpv.sh b/scripts/build-libmpv.sh index 25b97e9..4f0868d 100755 --- a/scripts/build-libmpv.sh +++ b/scripts/build-libmpv.sh @@ -117,6 +117,19 @@ case "$PLATFORM" in exit 1 fi + # Check audio dev packages — at least one is required for playback with sound + AUDIO_FOUND=false + for lib in libpulse alsa libpipewire-0.3; do + if pkg-config --exists "$lib" 2>/dev/null; then + AUDIO_FOUND=true + fi + done + if [[ "$AUDIO_FOUND" != "true" ]]; then + echo "Warning: No audio dev packages found. Install at least one of:" + echo " sudo apt-get install libpulse-dev libasound2-dev libpipewire-0.3-dev" + echo "Without these, libmpv will have no audio output support." + fi + # Clone mpv source (same version as macOS for consistency) MPV_SRC="$LIBS_DIR/mpv-src" MPV_TAG="v0.40.0" @@ -127,18 +140,39 @@ case "$PLATFORM" in echo " mpv source already present, skipping clone." fi - # Build + # Build with explicit audio + video output support. + # Audio: alsa/pulse/pipewire are auto-detected from dev headers; + # we enable them explicitly so the build fails if headers are missing + # rather than silently producing a libmpv without audio. BUILD_DIR="$MPV_SRC/build-linux" echo " Running meson setup..." - meson setup "$BUILD_DIR" "$MPV_SRC" \ - --buildtype=release \ - --wipe \ - -Dlibmpv=true \ - -Dgl=enabled \ - -Dvulkan=disabled \ - -Dwayland=enabled \ - -Dx11=enabled \ + + MESON_ARGS=( + --buildtype=release + --wipe + -Dlibmpv=true + -Dgl=enabled + -Dvulkan=disabled + -Dwayland=enabled + -Dx11=enabled -Degl=enabled + ) + + # Enable audio outputs that have dev headers available + if pkg-config --exists alsa 2>/dev/null; then + MESON_ARGS+=(-Dalsa=enabled) + echo " Audio: ALSA enabled" + fi + if pkg-config --exists libpulse 2>/dev/null; then + MESON_ARGS+=(-Dpulse=enabled) + echo " Audio: PulseAudio enabled" + fi + if pkg-config --exists libpipewire-0.3 2>/dev/null; then + MESON_ARGS+=(-Dpipewire=enabled) + echo " Audio: PipeWire enabled" + fi + + meson setup "$BUILD_DIR" "$MPV_SRC" "${MESON_ARGS[@]}" echo " Building libmpv.so (this takes a few minutes)..." ninja -C "$BUILD_DIR" libmpv.so.2 diff --git a/scripts/bundle-libmpv-linux.sh b/scripts/bundle-libmpv-linux.sh index 63cf5fc..c7fc002 100755 --- a/scripts/bundle-libmpv-linux.sh +++ b/scripts/bundle-libmpv-linux.sh @@ -25,9 +25,13 @@ if ! command -v patchelf &>/dev/null; then exit 1 fi -# Find libmpv — prefer system pkg-config, then libs/linux/ +# Find libmpv — prefer libs/linux/ (source build), then system pkg-config. +# Source build is preferred because it includes audio outputs we control. LIBMPV_PATH="" -if pkg-config --exists mpv 2>/dev/null; then +if [[ -f "$WORKSPACE_ROOT/libs/linux/libmpv.so" ]]; then + LIBMPV_PATH="$WORKSPACE_ROOT/libs/linux/libmpv.so" + echo " Using source-built libmpv from libs/linux/" +elif pkg-config --exists mpv 2>/dev/null; then LIBMPV_DIR=$(pkg-config --variable=libdir mpv 2>/dev/null || echo "") if [[ -n "$LIBMPV_DIR" ]]; then # Try versioned names first (libmpv.so.2, libmpv.so.1), then unversioned @@ -39,11 +43,8 @@ if pkg-config --exists mpv 2>/dev/null; then done fi fi -if [[ -z "$LIBMPV_PATH" && -f "$WORKSPACE_ROOT/libs/linux/libmpv.so" ]]; then - LIBMPV_PATH="$WORKSPACE_ROOT/libs/linux/libmpv.so" -fi if [[ -z "$LIBMPV_PATH" ]]; then - echo "Error: libmpv.so not found via pkg-config or libs/linux/" + echo "Error: libmpv.so not found in libs/linux/ or via pkg-config" exit 1 fi @@ -69,20 +70,63 @@ SYSTEM_LIBS_RE+="|libGL\.so|libEGL\.so|libGLX|libGLdispatch" SYSTEM_LIBS_RE+="|libgtk|libgdk|libglib|libgobject|libgio|libpango|libcairo|libatk" SYSTEM_LIBS_RE+="|libdbus|libsystemd|libfontconfig|libfreetype" -# Resolve and copy transitive multimedia dependencies -ldd "$LIBMPV_PATH" | grep "=> /" | awk '{print $3}' | while read -r dep; do - basename_dep=$(basename "$dep") - if echo "$basename_dep" | grep -qE "$SYSTEM_LIBS_RE"; then - continue - fi - if [[ ! -f "$BUNDLE_DIR/$basename_dep" ]]; then - cp "$dep" "$BUNDLE_DIR/" - echo " bundled: $basename_dep" - fi -done +# Collect all libs to bundle (deduplicated) +declare -A BUNDLED_LIBS + +# bundle_deps: resolve and collect transitive dependencies of a shared library +bundle_deps() { + local lib="$1" + ldd "$lib" 2>/dev/null | grep "=> /" | awk '{print $3}' | while read -r dep; do + basename_dep=$(basename "$dep") + if echo "$basename_dep" | grep -qE "$SYSTEM_LIBS_RE"; then + continue + fi + if [[ ! -f "$BUNDLE_DIR/$basename_dep" ]]; then + cp "$dep" "$BUNDLE_DIR/" + echo " bundled: $basename_dep" + # Recursively resolve deps of this library too (one level deep for audio libs) + ldd "$dep" 2>/dev/null | grep "=> /" | awk '{print $3}' | while read -r subdep; do + sub_basename=$(basename "$subdep") + if echo "$sub_basename" | grep -qE "$SYSTEM_LIBS_RE"; then + continue + fi + if [[ ! -f "$BUNDLE_DIR/$sub_basename" ]]; then + cp "$subdep" "$BUNDLE_DIR/" + echo " bundled: $sub_basename (transitive)" + fi + done + fi + done +} + +# Bundle libmpv's direct and transitive dependencies +bundle_deps "$LIBMPV_PATH" -# Set RPATH so the binary and libmpv find co-located libs +# Set RPATH so the binary and all bundled libs find co-located libs patchelf --set-rpath '$ORIGIN' "$BINARY" patchelf --set-rpath '$ORIGIN' "$BUNDLE_DIR/libmpv.so" +# Also set RPATH on all bundled .so files so they can find each other +for so in "$BUNDLE_DIR"/*.so*; do + if [[ -f "$so" && ! -L "$so" ]]; then + patchelf --set-rpath '$ORIGIN' "$so" 2>/dev/null || true + fi +done + +# Verify audio output support +echo "" +echo "==> Verifying bundled audio libraries..." +AUDIO_LIBS=0 +for pattern in libpulse libasound libpipewire; do + if ls "$BUNDLE_DIR"/${pattern}* 2>/dev/null | head -1 >/dev/null; then + echo " ✓ Found ${pattern}" + AUDIO_LIBS=$((AUDIO_LIBS + 1)) + fi +done +if [[ $AUDIO_LIBS -eq 0 ]]; then + echo " ⚠ WARNING: No audio libraries bundled. AppImage may have no audio output." + echo " Install audio dev packages and rebuild libmpv: sudo apt-get install libpulse-dev libasound2-dev" +fi + +echo "" echo " Done. Bundled libs placed alongside binary in $BUNDLE_DIR" From 24729c6cd1196c3d29daa9dcdd0197291711ddff Mon Sep 17 00:00:00 2001 From: Max Boksem <15022344+MaxMB15@users.noreply.github.com> Date: Wed, 8 Apr 2026 22:09:49 +0200 Subject: [PATCH 3/8] =?UTF-8?q?fix:=20address=20review=20feedback=20?= =?UTF-8?q?=E2=80=94=20libplacebo=20dep,=20recursive=20bundling,=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add libplacebo-dev to CI deps (required by mpv 0.40.0); disable via meson flag when not available for local builds - Implement proper recursive dependency walk in bundle script (fixpoint loop using bundle dir as visited set) instead of one-level-deep nesting - Remove unused BUNDLED_LIBS declaration - Fix misleading comment about build failing without audio headers - Remove bogus PKG_CONFIG_PATH pointing to nonexistent pkgconfig/ dir Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build.yml | 3 +- .github/workflows/release.yml | 4 +-- scripts/build-libmpv.sh | 19 ++++++++--- scripts/bundle-libmpv-linux.sh | 59 ++++++++++++++++++---------------- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 70e81da..73d9f7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -262,7 +262,8 @@ jobs: patchelf rpm \ meson ninja-build \ libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev \ - libass-dev libpulse-dev libasound2-dev + libass-dev libpulse-dev libasound2-dev \ + libplacebo-dev - name: Build libmpv from source run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a997c1d..32caf09 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -235,13 +235,13 @@ jobs: patchelf rpm \ meson ninja-build \ libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev \ - libass-dev libpulse-dev libasound2-dev + libass-dev libpulse-dev libasound2-dev \ + libplacebo-dev - name: Build libmpv from source run: | ./scripts/build-libmpv.sh linux echo "LD_LIBRARY_PATH=$(pwd)/libs/linux:${LD_LIBRARY_PATH:-}" >> "$GITHUB_ENV" - echo "PKG_CONFIG_PATH=$(pwd)/libs/linux/pkgconfig:${PKG_CONFIG_PATH:-}" >> "$GITHUB_ENV" - name: Prepare signing key run: | diff --git a/scripts/build-libmpv.sh b/scripts/build-libmpv.sh index 4f0868d..4362143 100755 --- a/scripts/build-libmpv.sh +++ b/scripts/build-libmpv.sh @@ -140,10 +140,10 @@ case "$PLATFORM" in echo " mpv source already present, skipping clone." fi - # Build with explicit audio + video output support. - # Audio: alsa/pulse/pipewire are auto-detected from dev headers; - # we enable them explicitly so the build fails if headers are missing - # rather than silently producing a libmpv without audio. + # Build with audio + video output support. + # Audio backends are conditionally enabled based on available dev headers. + # If no audio dev headers are found, the build will warn but continue — + # the AUDIO_FOUND check above already warns the user. BUILD_DIR="$MPV_SRC/build-linux" echo " Running meson setup..." @@ -158,7 +158,16 @@ case "$PLATFORM" in -Degl=enabled ) - # Enable audio outputs that have dev headers available + # libplacebo is a required dependency for mpv 0.40.0. + # Disable it to avoid needing the dev package (mpv falls back to internal shaders). + if ! pkg-config --exists libplacebo 2>/dev/null; then + MESON_ARGS+=(-Dlibplacebo=disabled) + echo " libplacebo not found, disabling (mpv will use internal shaders)" + fi + + # Enable audio outputs that have dev headers available. + # Using -D=enabled makes meson fail if the dep can't be satisfied, + # catching misconfigured build environments early. if pkg-config --exists alsa 2>/dev/null; then MESON_ARGS+=(-Dalsa=enabled) echo " Audio: ALSA enabled" diff --git a/scripts/bundle-libmpv-linux.sh b/scripts/bundle-libmpv-linux.sh index c7fc002..a1bd845 100755 --- a/scripts/bundle-libmpv-linux.sh +++ b/scripts/bundle-libmpv-linux.sh @@ -70,37 +70,42 @@ SYSTEM_LIBS_RE+="|libGL\.so|libEGL\.so|libGLX|libGLdispatch" SYSTEM_LIBS_RE+="|libgtk|libgdk|libglib|libgobject|libgio|libpango|libcairo|libatk" SYSTEM_LIBS_RE+="|libdbus|libsystemd|libfontconfig|libfreetype" -# Collect all libs to bundle (deduplicated) -declare -A BUNDLED_LIBS - -# bundle_deps: resolve and collect transitive dependencies of a shared library -bundle_deps() { - local lib="$1" - ldd "$lib" 2>/dev/null | grep "=> /" | awk '{print $3}' | while read -r dep; do - basename_dep=$(basename "$dep") - if echo "$basename_dep" | grep -qE "$SYSTEM_LIBS_RE"; then - continue - fi - if [[ ! -f "$BUNDLE_DIR/$basename_dep" ]]; then - cp "$dep" "$BUNDLE_DIR/" - echo " bundled: $basename_dep" - # Recursively resolve deps of this library too (one level deep for audio libs) - ldd "$dep" 2>/dev/null | grep "=> /" | awk '{print $3}' | while read -r subdep; do - sub_basename=$(basename "$subdep") - if echo "$sub_basename" | grep -qE "$SYSTEM_LIBS_RE"; then - continue - fi - if [[ ! -f "$BUNDLE_DIR/$sub_basename" ]]; then - cp "$subdep" "$BUNDLE_DIR/" - echo " bundled: $sub_basename (transitive)" - fi - done +# bundle_all_deps: recursively resolve and copy dependencies to a fixpoint. +# Uses the bundle dir itself as the visited set — if a .so already exists +# there, it's been processed and won't be traversed again. +bundle_all_deps() { + local queue=("$@") + while [[ ${#queue[@]} -gt 0 ]]; do + local lib="${queue[0]}" + queue=("${queue[@]:1}") + ldd "$lib" 2>/dev/null | grep "=> /" | awk '{print $3}' | while read -r dep; do + local basename_dep + basename_dep=$(basename "$dep") + if echo "$basename_dep" | grep -qE "$SYSTEM_LIBS_RE"; then + continue + fi + if [[ ! -f "$BUNDLE_DIR/$basename_dep" ]]; then + cp "$dep" "$BUNDLE_DIR/" + echo " bundled: $basename_dep" + # Append to queue file so the outer loop picks it up + echo "$BUNDLE_DIR/$basename_dep" >> "$QUEUE_FILE" + fi + done + # Read any newly discovered libs back into the queue + if [[ -f "$QUEUE_FILE" ]]; then + while IFS= read -r newlib; do + queue+=("$newlib") + done < "$QUEUE_FILE" + rm -f "$QUEUE_FILE" fi done } -# Bundle libmpv's direct and transitive dependencies -bundle_deps "$LIBMPV_PATH" +QUEUE_FILE=$(mktemp) +trap 'rm -f "$QUEUE_FILE"' EXIT + +# Bundle libmpv and all transitive dependencies +bundle_all_deps "$LIBMPV_PATH" # Set RPATH so the binary and all bundled libs find co-located libs patchelf --set-rpath '$ORIGIN' "$BINARY" From 07b4204ee33d0dbeef956e0b1ac12ed227b7a4cf Mon Sep 17 00:00:00 2001 From: Max Boksem <15022344+MaxMB15@users.noreply.github.com> Date: Wed, 8 Apr 2026 22:14:30 +0200 Subject: [PATCH 4/8] fix: add missing X11/Wayland dev packages for libmpv source build mpv 0.40.0 requires xscrnsaver, xext, xpresent, xrandr when X11 is enabled and wayland-egl when Wayland is enabled. Also disable optional features (lua, javascript, caca, sdl2, drm, jack, sndio, openal) to avoid auto-detection pulling in unneeded deps. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build.yml | 4 +++- .github/workflows/release.yml | 4 +++- scripts/build-libmpv.sh | 27 ++++++++++++++++++--------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 73d9f7b..83e00e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -263,7 +263,9 @@ jobs: meson ninja-build \ libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev \ libass-dev libpulse-dev libasound2-dev \ - libplacebo-dev + libplacebo-dev \ + libx11-dev libxss-dev libxext-dev libxpresent-dev libxrandr-dev \ + libwayland-dev - name: Build libmpv from source run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 32caf09..d139003 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -236,7 +236,9 @@ jobs: meson ninja-build \ libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev \ libass-dev libpulse-dev libasound2-dev \ - libplacebo-dev + libplacebo-dev \ + libx11-dev libxss-dev libxext-dev libxpresent-dev libxrandr-dev \ + libwayland-dev - name: Build libmpv from source run: | diff --git a/scripts/build-libmpv.sh b/scripts/build-libmpv.sh index 4362143..9850f86 100755 --- a/scripts/build-libmpv.sh +++ b/scripts/build-libmpv.sh @@ -151,19 +151,28 @@ case "$PLATFORM" in --buildtype=release --wipe -Dlibmpv=true + # Video output: EGL/GL on X11 and Wayland -Dgl=enabled - -Dvulkan=disabled - -Dwayland=enabled - -Dx11=enabled -Degl=enabled + -Dx11=enabled + -Dwayland=enabled + -Dvulkan=disabled + # Disable optional features we don't need — avoids auto-detection pulling + # in deps that may not be on the CI runner (libplacebo, libXss, lua, etc.) + -Dlibplacebo=disabled + -Dlua=disabled + -Djavascript=disabled + -Dcaca=disabled + -Dsdl2=disabled + -Ddrm=disabled + -Djack=disabled + -Doss-audio=disabled + -Dsndio=disabled + -Dopenal=disabled ) - # libplacebo is a required dependency for mpv 0.40.0. - # Disable it to avoid needing the dev package (mpv falls back to internal shaders). - if ! pkg-config --exists libplacebo 2>/dev/null; then - MESON_ARGS+=(-Dlibplacebo=disabled) - echo " libplacebo not found, disabling (mpv will use internal shaders)" - fi + # Note: X11 support requires: libx11-dev libxss-dev libxext-dev libxpresent-dev libxrandr-dev + # Wayland EGL requires: libwayland-dev # Enable audio outputs that have dev headers available. # Using -D=enabled makes meson fail if the dep can't be satisfied, From ab10dce4b44a3b3437e94f38c51975212c97b096 Mon Sep 17 00:00:00 2001 From: Max Boksem <15022344+MaxMB15@users.noreply.github.com> Date: Wed, 8 Apr 2026 22:18:46 +0200 Subject: [PATCH 5/8] fix: remove invalid -Dlibplacebo=disabled meson option libplacebo is a hard requirement of mpv 0.40.0, not a toggleable meson option. libplacebo-dev is already in CI deps. Co-Authored-By: Claude Opus 4.6 --- scripts/build-libmpv.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/build-libmpv.sh b/scripts/build-libmpv.sh index 9850f86..4e9328a 100755 --- a/scripts/build-libmpv.sh +++ b/scripts/build-libmpv.sh @@ -158,8 +158,9 @@ case "$PLATFORM" in -Dwayland=enabled -Dvulkan=disabled # Disable optional features we don't need — avoids auto-detection pulling - # in deps that may not be on the CI runner (libplacebo, libXss, lua, etc.) - -Dlibplacebo=disabled + # in deps that may not be on the CI runner (lua, javascript, etc.) + # Note: libplacebo is a hard requirement of mpv 0.40.0 (not a meson option), + # so libplacebo-dev must be installed. -Dlua=disabled -Djavascript=disabled -Dcaca=disabled From e24f9edf23b8964be3495b76fc752224c3f10313 Mon Sep 17 00:00:00 2001 From: Max Boksem <15022344+MaxMB15@users.noreply.github.com> Date: Wed, 8 Apr 2026 22:22:50 +0200 Subject: [PATCH 6/8] =?UTF-8?q?fix:=20build=20all=20ninja=20targets=20?= =?UTF-8?q?=E2=80=94=20libmpv.so.2=20target=20name=20varies=20by=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- scripts/build-libmpv.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/build-libmpv.sh b/scripts/build-libmpv.sh index 4e9328a..aca2742 100755 --- a/scripts/build-libmpv.sh +++ b/scripts/build-libmpv.sh @@ -194,7 +194,8 @@ case "$PLATFORM" in meson setup "$BUILD_DIR" "$MPV_SRC" "${MESON_ARGS[@]}" echo " Building libmpv.so (this takes a few minutes)..." - ninja -C "$BUILD_DIR" libmpv.so.2 + # Build all targets — the .so name varies by mpv version/config + ninja -C "$BUILD_DIR" # Copy .so to libs/linux/ SO=$(find "$BUILD_DIR" -name "libmpv.so*" -not -type l | head -1) From 5b711b7833fbeea747e5043d90a91e5269a1ce2a Mon Sep 17 00:00:00 2001 From: Max Boksem <15022344+MaxMB15@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:05:56 +0200 Subject: [PATCH 7/8] fix(build): use -type f in find to avoid matching meson .p build dirs The pattern 'libmpv.so*' matched both the actual libmpv.so.2.5.0 file and the libmpv.so.2.5.0.p directory (meson per-target build dir). -not -type l only excluded symlinks, not directories, so cp failed with 'omitting directory'. Switch to -type f to match only regular files. Made-with: Cursor --- scripts/build-libmpv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build-libmpv.sh b/scripts/build-libmpv.sh index aca2742..488eb1a 100755 --- a/scripts/build-libmpv.sh +++ b/scripts/build-libmpv.sh @@ -198,7 +198,7 @@ case "$PLATFORM" in ninja -C "$BUILD_DIR" # Copy .so to libs/linux/ - SO=$(find "$BUILD_DIR" -name "libmpv.so*" -not -type l | head -1) + SO=$(find "$BUILD_DIR" -name "libmpv.so*" -type f | head -1) if [[ -z "$SO" ]]; then echo "Error: libmpv.so not found after build" exit 1 From 8c227cf4c7c70ba6c64e512dd841611db2d4dbbe Mon Sep 17 00:00:00 2001 From: Max Boksem <15022344+MaxMB15@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:31:09 +0200 Subject: [PATCH 8/8] fix(bundle): use process substitution to fix silent pipefail in bundle_all_deps With 'set -euo pipefail', ldd|grep|awk|while fails silently when grep finds no '=> /' lines (libs with only linux-vdso/ld-linux deps). The pipeline's while loop also runs in a subshell, so queue+= updates were lost and the QUEUE_FILE workaround was needed. Switch to process substitution (< <(... || true)): - '|| true' suppresses grep exit-1 on zero matches - while body runs in the current shell so queue+= propagates directly - QUEUE_FILE and its trap are no longer needed Also add explicit error messages to patchelf calls so failures are visible in CI logs instead of being swallowed by set -e. Made-with: Cursor --- scripts/bundle-libmpv-linux.sh | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/scripts/bundle-libmpv-linux.sh b/scripts/bundle-libmpv-linux.sh index a1bd845..da1b30d 100755 --- a/scripts/bundle-libmpv-linux.sh +++ b/scripts/bundle-libmpv-linux.sh @@ -73,12 +73,18 @@ SYSTEM_LIBS_RE+="|libdbus|libsystemd|libfontconfig|libfreetype" # bundle_all_deps: recursively resolve and copy dependencies to a fixpoint. # Uses the bundle dir itself as the visited set — if a .so already exists # there, it's been processed and won't be traversed again. +# +# Uses process substitution (< <(...)) instead of a pipeline so that: +# 1. The while loop body runs in the current shell — queue+= propagates. +# 2. "|| true" after the process substitution suppresses grep exit-1 when a +# lib has no "=> /" lines (e.g. only linux-vdso / ld-linux deps), which +# would otherwise trigger set -euo pipefail and kill the script silently. bundle_all_deps() { local queue=("$@") while [[ ${#queue[@]} -gt 0 ]]; do local lib="${queue[0]}" queue=("${queue[@]:1}") - ldd "$lib" 2>/dev/null | grep "=> /" | awk '{print $3}' | while read -r dep; do + while IFS= read -r dep; do local basename_dep basename_dep=$(basename "$dep") if echo "$basename_dep" | grep -qE "$SYSTEM_LIBS_RE"; then @@ -87,29 +93,21 @@ bundle_all_deps() { if [[ ! -f "$BUNDLE_DIR/$basename_dep" ]]; then cp "$dep" "$BUNDLE_DIR/" echo " bundled: $basename_dep" - # Append to queue file so the outer loop picks it up - echo "$BUNDLE_DIR/$basename_dep" >> "$QUEUE_FILE" + queue+=("$BUNDLE_DIR/$basename_dep") fi - done - # Read any newly discovered libs back into the queue - if [[ -f "$QUEUE_FILE" ]]; then - while IFS= read -r newlib; do - queue+=("$newlib") - done < "$QUEUE_FILE" - rm -f "$QUEUE_FILE" - fi + done < <(ldd "$lib" 2>/dev/null | grep "=> /" | awk '{print $3}' || true) done } -QUEUE_FILE=$(mktemp) -trap 'rm -f "$QUEUE_FILE"' EXIT - # Bundle libmpv and all transitive dependencies bundle_all_deps "$LIBMPV_PATH" # Set RPATH so the binary and all bundled libs find co-located libs -patchelf --set-rpath '$ORIGIN' "$BINARY" -patchelf --set-rpath '$ORIGIN' "$BUNDLE_DIR/libmpv.so" +echo " Setting RPATH on binary..." +patchelf --set-rpath '$ORIGIN' "$BINARY" \ + || { echo "Error: patchelf --set-rpath failed on $BINARY"; exit 1; } +patchelf --set-rpath '$ORIGIN' "$BUNDLE_DIR/libmpv.so" \ + || { echo "Error: patchelf --set-rpath failed on $BUNDLE_DIR/libmpv.so"; exit 1; } # Also set RPATH on all bundled .so files so they can find each other for so in "$BUNDLE_DIR"/*.so*; do