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
15 changes: 13 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,22 @@ 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 \
libplacebo-dev \
libx11-dev libxss-dev libxext-dev libxpresent-dev libxrandr-dev \
libwayland-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: |
Expand Down
15 changes: 13 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,22 @@ 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 \
libplacebo-dev \
libx11-dev libxss-dev libxext-dev libxpresent-dev libxrandr-dev \
libwayland-dev

- name: Build libmpv from source
run: |
./scripts/build-libmpv.sh linux
echo "LD_LIBRARY_PATH=$(pwd)/libs/linux:${LD_LIBRARY_PATH:-}" >> "$GITHUB_ENV"

Comment on lines +243 to 247
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

release.yml exports PKG_CONFIG_PATH=$(pwd)/libs/linux/pkgconfig, but scripts/build-libmpv.sh linux currently only copies libmpv.so into libs/linux/ and does not create a pkgconfig/ directory or install a .pc file there. This env var looks unused/misleading; either drop it or add a step that actually generates/copies the mpv/libmpv pkg-config file into that directory if something depends on it.

Copilot uses AI. Check for mistakes.
- name: Prepare signing key
run: |
Expand Down
32 changes: 25 additions & 7 deletions apps/desktop/src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1270,14 +1270,32 @@ pub async fn package_update<R: Runtime>(app: AppHandle<R>) -> 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")
Expand Down
18 changes: 16 additions & 2 deletions apps/desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
}
}
}
Expand Down
28 changes: 16 additions & 12 deletions crates/tauri-plugin-mpv/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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")]
Expand Down
9 changes: 6 additions & 3 deletions crates/tauri-plugin-mpv/src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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"),
Expand All @@ -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"),
Expand Down
76 changes: 65 additions & 11 deletions scripts/build-libmpv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -127,24 +140,65 @@ case "$PLATFORM" in
echo " mpv source already present, skipping clone."
fi

# Build
# 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..."
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
# Video output: EGL/GL on X11 and Wayland
-Dgl=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 (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
-Dsdl2=disabled
-Ddrm=disabled
-Djack=disabled
-Doss-audio=disabled
-Dsndio=disabled
-Dopenal=disabled
)

# 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<backend>=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"
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
# 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)
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
Expand Down
83 changes: 65 additions & 18 deletions scripts/bundle-libmpv-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -69,20 +70,66 @@ 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"
# 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}")
while IFS= 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"
queue+=("$BUNDLE_DIR/$basename_dep")
fi
done < <(ldd "$lib" 2>/dev/null | grep "=> /" | awk '{print $3}' || true)
done
}

# Bundle libmpv and all transitive dependencies
bundle_all_deps "$LIBMPV_PATH"

# Set RPATH so the binary and all bundled libs find co-located libs
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
if [[ -f "$so" && ! -L "$so" ]]; then
patchelf --set-rpath '$ORIGIN' "$so" 2>/dev/null || true
fi
done

# Set RPATH so the binary and libmpv find co-located libs
patchelf --set-rpath '$ORIGIN' "$BINARY"
patchelf --set-rpath '$ORIGIN' "$BUNDLE_DIR/libmpv.so"
# 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"
Loading