From 771006434867755ba6300b18429b8c326dd780f4 Mon Sep 17 00:00:00 2001 From: Maxim Raznatovski Date: Wed, 26 Nov 2025 10:37:41 +0100 Subject: [PATCH] Pre-build PyAV wheel in base image to speed up release builds (#2682) --- .github/workflows/release.yml | 4 ++-- Dockerfile | 21 ++++++++++---------- Dockerfile.base | 36 +++++++++++++++++++++++++++-------- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 760da453..e6fe5f73 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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: diff --git a/Dockerfile b/Dockerfile index d5a007c9..d956bd00 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/Dockerfile.base b/Dockerfile.base index 6975b5df..e89ffd12 100644 --- a/Dockerfile.base +++ b/Dockerfile.base @@ -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 -- 2.34.1