Pre-build PyAV wheel in base image to speed up release builds (#2682)
authorMaxim Raznatovski <nda.mr43@gmail.com>
Wed, 26 Nov 2025 09:37:41 +0000 (10:37 +0100)
committerGitHub <noreply@github.com>
Wed, 26 Nov 2025 09:37:41 +0000 (10:37 +0100)
.github/workflows/release.yml
Dockerfile
Dockerfile.base

index 760da453bde17a00678cd6c83f030dc71b307f28..e6fe5f731ee99ba4d52796044ad441d14a27445e 100644 (file)
@@ -34,8 +34,8 @@ on:
 env:
   PYTHON_VERSION: "3.12"
   BASE_IMAGE_VERSION_STABLE: "1.3.1"
-  BASE_IMAGE_VERSION_BETA: "1.4.8"
-  BASE_IMAGE_VERSION_NIGHTLY: "1.4.8"
+  BASE_IMAGE_VERSION_BETA: "1.4.9"
+  BASE_IMAGE_VERSION_NIGHTLY: "1.4.9"
 
 jobs:
   preflight-checks:
index d5a007c993057143fe6df2830410fe88d4545a5c..d956bd00eb3f728697ba03738a559f51e18bd2ca 100644 (file)
@@ -11,13 +11,6 @@ COPY requirements_all.txt .
 # ensure UV is installed
 COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
 
-# Install build tools for PyAV compilation (for aioresonate)
-RUN apt-get update && apt-get install -y --no-install-recommends \
-    gcc \
-    g++ \
-    python3-dev \
-    && rm -rf /var/lib/apt/lists/*
-
 # create venv which will be copied to the final image
 ENV VIRTUAL_ENV=/app/venv
 RUN uv venv $VIRTUAL_ENV
@@ -28,10 +21,16 @@ RUN uv venv $VIRTUAL_ENV
 RUN uv pip install \
     -r requirements_all.txt
 
-# Reinstall PyAV from source to use system FFmpeg instead of bundled FFmpeg
-# Use the version already resolved by requirements_all.txt to ensure compatibility
-RUN uv pip install --no-binary av --force-reinstall --no-deps \
-    "av==$($VIRTUAL_ENV/bin/python -c 'import importlib.metadata; print(importlib.metadata.version("av"))')"
+# Install PyAV from pre-built wheel (built against system FFmpeg in base image)
+# First verify the wheel version matches what pip resolved to avoid version mismatch
+RUN REQUIRED_VERSION=$($VIRTUAL_ENV/bin/python -c "import importlib.metadata; print(importlib.metadata.version('av'))") && \
+    WHEEL_VERSION=$(ls /usr/local/share/pyav-wheels/av*.whl | grep -oP 'av-\K[0-9.]+') && \
+    if [ "$REQUIRED_VERSION" != "$WHEEL_VERSION" ]; then \
+      echo "ERROR: PyAV version mismatch! Requirements need $REQUIRED_VERSION but base image has $WHEEL_VERSION" && \
+      echo "Please rebuild the base image with the correct PyAV version." && \
+      exit 1; \
+    fi && \
+    uv pip install --force-reinstall --no-deps /usr/local/share/pyav-wheels/av*.whl
 
 # Install Music Assistant from prebuilt wheel
 ARG MASS_VERSION
index 6975b5df68da6f9ddb6427253ad7e890c2941144..e89ffd121fc397032f700763ccf94981816ae1b8 100644 (file)
@@ -101,6 +101,32 @@ RUN set -x \
 
 ##################################################################################################
 
+# PyAV wheel builder - builds PyAV against the custom FFmpeg
+FROM python:3.13-slim-bookworm AS pyav-builder
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+    gcc g++ python3-dev pkg-config && rm -rf /var/lib/apt/lists/*
+
+COPY --from=ffmpeg-builder /usr/local/lib/libav*.so* /usr/local/lib/
+COPY --from=ffmpeg-builder /usr/local/lib/libsw*.so* /usr/local/lib/
+COPY --from=ffmpeg-builder /usr/local/lib/libpostproc.so* /usr/local/lib/
+COPY --from=ffmpeg-builder /usr/local/include/ /usr/local/include/
+COPY --from=ffmpeg-builder /usr/local/lib/pkgconfig/ /usr/local/lib/pkgconfig/
+
+RUN ldconfig
+ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
+
+# Resolve PyAV version from aioresonate's dependencies and build wheel
+RUN PYAV_VERSION=$(pip install --dry-run aioresonate 2>&1 | grep -oP 'av-\K[0-9.]+' | head -1) && \
+    if [ -z "$PYAV_VERSION" ]; then \
+        echo "ERROR: Failed to detect PyAV version from aioresonate dependencies" >&2; \
+        exit 1; \
+    fi && \
+    echo "Building PyAV version: ${PYAV_VERSION}" && \
+    pip wheel --no-binary av av==${PYAV_VERSION} -w /wheels/
+
+##################################################################################################
+
 FROM python:3.13-slim-bookworm
 
 # Enable non-free and contrib repositories for codec libraries
@@ -169,8 +195,6 @@ RUN set -x \
         # AirPlay receiver dependencies (shairport-sync)
         libconfig9 \
         libpopt0 \
-        # pkg-config needed for PyAV (for aioresonate) to find system FFmpeg
-        pkg-config \
     && apt-get clean \
     && rm -rf /var/lib/apt/lists/*
 
@@ -195,12 +219,8 @@ COPY --from=ffmpeg-builder /usr/local/lib/libav*.so* /usr/local/lib/
 COPY --from=ffmpeg-builder /usr/local/lib/libsw*.so* /usr/local/lib/
 COPY --from=ffmpeg-builder /usr/local/lib/libpostproc.so* /usr/local/lib/
 
-# Copy FFmpeg headers and pkg-config files needed for PyAV compilation (adds around 2 MB)
-COPY --from=ffmpeg-builder /usr/local/include/ /usr/local/include/
-COPY --from=ffmpeg-builder /usr/local/lib/pkgconfig/ /usr/local/lib/pkgconfig/
-
-# Set PKG_CONFIG_PATH so pkg-config can find FFmpeg
-ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
+# Copy pre-built PyAV wheel for use by downstream images
+COPY --from=pyav-builder /wheels/ /usr/local/share/pyav-wheels/
 
 # Update shared library cache and verify FFmpeg
 RUN ldconfig && ffmpeg -version && ffprobe -version