/
/
/
1# syntax=docker/dockerfile:1
2
3# Python version is centralized in .python-version at repo root.
4# Workflows read that file and pass it here via --build-arg PYTHON_VERSION=...
5# The default below is a safety net for manual `docker build` without --build-arg.
6ARG PYTHON_VERSION=3.14
7
8# BASE docker image for music assistant container
9# This image forms the base for the final image and is not meant to be used directly
10# NOTE that the dev add-on is also based on this base image
11
12FROM python:${PYTHON_VERSION}-slim-trixie AS ffmpeg-builder
13
14# Enable non-free and contrib repositories for FDK-AAC and other codecs
15# (trixie uses the deb822 sources format)
16RUN sed -i 's/^Components: .*/Components: main contrib non-free non-free-firmware/' /etc/apt/sources.list.d/debian.sources
17
18# Install build dependencies for FFmpeg
19RUN apt-get update && apt-get install -y --no-install-recommends \
20 build-essential \
21 pkg-config \
22 yasm \
23 nasm \
24 git \
25 wget \
26 ca-certificates \
27 # Audio codec libraries
28 libfdk-aac-dev \
29 libmp3lame-dev \
30 libopus-dev \
31 libvorbis-dev \
32 libsoxr-dev \
33 libspeex-dev \
34 libtwolame-dev \
35 libvo-amrwbenc-dev \
36 libopencore-amrnb-dev \
37 libopencore-amrwb-dev \
38 libshine-dev \
39 # Audio processing and filters
40 librubberband-dev \
41 libbs2b-dev \
42 libsamplerate0-dev \
43 libmysofa-dev \
44 libjack-jackd2-dev \
45 libpulse-dev \
46 # Additional libraries
47 libbluray-dev \
48 libxml2-dev \
49 libssh-dev \
50 liblzma-dev \
51 # SSL/TLS support for HTTPS
52 libssl-dev \
53 && rm -rf /var/lib/apt/lists/*
54
55# Build FFmpeg 7.1.2 from source with comprehensive audio codec support
56ARG FFMPEG_VERSION=7.1.2
57RUN set -x \
58 && wget -q "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz" -O /tmp/ffmpeg.tar.xz \
59 && tar -xJf /tmp/ffmpeg.tar.xz -C /tmp \
60 && cd /tmp/ffmpeg-${FFMPEG_VERSION} \
61 && ./configure \
62 --prefix=/usr/local \
63 --enable-gpl \
64 --enable-nonfree \
65 --enable-version3 \
66 # Audio codecs (comprehensive support)
67 --enable-libfdk-aac \
68 --enable-libmp3lame \
69 --enable-libopus \
70 --enable-libvorbis \
71 --enable-libspeex \
72 --enable-libtwolame \
73 --enable-libshine \
74 --enable-libopencore-amrnb \
75 --enable-libopencore-amrwb \
76 --enable-libvo-amrwbenc \
77 # Audio filters and resampling
78 --enable-libsoxr \
79 --enable-librubberband \
80 --enable-libbs2b \
81 --enable-libmysofa \
82 --enable-libjack \
83 --enable-libpulse \
84 # SSL/TLS support for HTTPS
85 --enable-openssl \
86 # Additional libraries for playlist and network support
87 --enable-libxml2 \
88 --enable-libssh \
89 --enable-lzma \
90 # Optimizations
91 --enable-runtime-cpudetect \
92 # Disable unnecessary features for smaller build
93 --disable-doc \
94 --disable-htmlpages \
95 --disable-manpages \
96 --disable-podpages \
97 --disable-txtpages \
98 --disable-debug \
99 --disable-static \
100 --enable-shared \
101 && make -j$(nproc) \
102 && make install \
103 && strip /usr/local/bin/ffmpeg /usr/local/bin/ffprobe \
104 && rm -rf /tmp/ffmpeg*
105
106##################################################################################################
107
108# PyAV wheel builder - builds PyAV against the custom FFmpeg
109FROM python:${PYTHON_VERSION}-slim-trixie AS pyav-builder
110
111RUN apt-get update && apt-get install -y --no-install-recommends \
112 gcc g++ python3-dev pkg-config && rm -rf /var/lib/apt/lists/*
113
114COPY --from=ffmpeg-builder /usr/local/lib/libav*.so* /usr/local/lib/
115COPY --from=ffmpeg-builder /usr/local/lib/libsw*.so* /usr/local/lib/
116COPY --from=ffmpeg-builder /usr/local/lib/libpostproc.so* /usr/local/lib/
117COPY --from=ffmpeg-builder /usr/local/include/ /usr/local/include/
118COPY --from=ffmpeg-builder /usr/local/lib/pkgconfig/ /usr/local/lib/pkgconfig/
119
120RUN ldconfig
121ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
122
123# Build PyAV wheel against the custom FFmpeg with the in the Sendspin provider manifest pinned version
124COPY music_assistant/providers/sendspin/manifest.json /tmp/sendspin_manifest.json
125RUN PYAV_VERSION=$(python -c "import json; reqs=json.load(open('/tmp/sendspin_manifest.json'))['requirements']; print(next(r.split('==')[1] for r in reqs if r.startswith('av==')))") && \
126 echo "Building PyAV version: ${PYAV_VERSION}" && \
127 pip wheel --no-binary av av==${PYAV_VERSION} -w /wheels/
128
129##################################################################################################
130
131# Build shairport-sync (airplay receiver provider) from source against this image's
132# libraries, using the same configuration as music_assistant/providers/airplay_receiver/bin/build_binaries.sh:
133# tinysvcmdns as embedded mDNS backend (no avahi), stdout/pipe outputs and metadata support.
134FROM python:${PYTHON_VERSION}-slim-trixie AS shairport-builder
135
136RUN apt-get update && apt-get install -y --no-install-recommends \
137 build-essential \
138 git \
139 autoconf \
140 automake \
141 libtool \
142 pkg-config \
143 libconfig-dev \
144 libpopt-dev \
145 libssl-dev \
146 libdbus-1-dev \
147 libglib2.0-dev \
148 ca-certificates \
149 && rm -rf /var/lib/apt/lists/*
150
151# Version and its commit SHA are the single source of truth (also read by
152# build_binaries.sh). Tags are mutable, so the fetched tag is verified against the
153# pinned SHA. Bump both when changing the version.
154ARG SHAIRPORT_VERSION=4.3.7
155ARG SHAIRPORT_SHA=0b1c4391ffd398e7b145eb4b98416261380adeea
156RUN set -x \
157 && git init /tmp/shairport-sync \
158 && cd /tmp/shairport-sync \
159 && git remote add origin https://github.com/mikebrady/shairport-sync.git \
160 && git fetch --depth 1 origin refs/tags/${SHAIRPORT_VERSION} \
161 && git checkout FETCH_HEAD \
162 && test "$(git rev-parse HEAD)" = "${SHAIRPORT_SHA}" \
163 && autoreconf -fi \
164 && ./configure \
165 --with-pipe \
166 --with-metadata \
167 --without-avahi \
168 --without-dns-sd \
169 --with-tinysvcmdns \
170 --with-ssl=openssl \
171 --with-stdout \
172 --sysconfdir=/etc \
173 && make -j$(nproc) \
174 && strip shairport-sync \
175 && cp shairport-sync /usr/local/bin/shairport-sync \
176 && rm -rf /tmp/shairport-sync
177
178##################################################################################################
179
180FROM python:${PYTHON_VERSION}-slim-trixie
181
182# Ensure UTF-8 encoding across system
183ENV LANG=C.UTF-8
184
185# Enable non-free and contrib repositories for codec libraries
186# (trixie uses the deb822 sources format)
187RUN sed -i 's/^Components: .*/Components: main contrib non-free non-free-firmware/' /etc/apt/sources.list.d/debian.sources
188
189# Install runtime dependencies
190RUN set -x \
191 && apt-get update \
192 && apt-get install -y --no-install-recommends \
193 ca-certificates \
194 libjemalloc2 \
195 tzdata \
196 wget \
197 # cifs utils and libnfs are needed for smb and nfs support (file provider)
198 cifs-utils \
199 libnfs14 \
200 nfs-common \
201 # airplay libraries
202 openssl \
203 libssl-dev \
204 libuuid1 \
205 libcurl4 \
206 libsodium23 \
207 libconfuse2 \
208 libevent-dev \
209 libjson-c5 \
210 libgcrypt20 \
211 # glib is needed by the shairport-sync binary (previously pulled in only
212 # indirectly via the pipewire/pulseaudio utils)
213 libglib2.0-0 \
214 # libsndfile needed for librosa audio file support (smartfades)
215 libsndfile1 \
216 # libchromaprint needed for AcoustID fingerprinting (acoustid_lookup)
217 libchromaprint1 \
218 # Audio codec runtime libraries (needed for FFmpeg)
219 libfdk-aac2 \
220 libmp3lame0 \
221 libopus0 \
222 libvorbis0a \
223 libvorbisenc2 \
224 libsoxr0 \
225 libspeex1 \
226 libtwolame0 \
227 libshine3 \
228 libopencore-amrnb0 \
229 libopencore-amrwb0 \
230 libvo-amrwbenc0 \
231 # Audio processing libraries
232 librubberband2 \
233 libbs2b0 \
234 libsamplerate0 \
235 libmysofa1 \
236 libjack-jackd2-0 \
237 libpulse0 \
238 # Additional libraries
239 libbluray2 \
240 libxml2 \
241 libssh-4 \
242 liblzma5 \
243 # Snapcast dependencies
244 libasound2 \
245 # pactl, used by helpers/pulse_capture.py to suspend/resume sinks
246 pulseaudio-utils \
247 # PulseAudio daemon for MA's private audio-capture server (spotify connect soloist)
248 pulseaudio \
249 libvorbisidec1 \
250 libflac14 \
251 libavahi-client3 \
252 libavahi-common3 \
253 # AirPlay receiver dependencies (shairport-sync)
254 libconfig11 \
255 libpopt0 \
256 && apt-get clean \
257 && rm -rf /var/lib/apt/lists/* \
258 && rm -f \
259 /usr/bin/parecord \
260 /usr/bin/paplay \
261 /usr/bin/parec \
262 /usr/bin/pamon \
263 /usr/bin/pacat \
264 /usr/bin/pasuspender \
265 /usr/bin/pacmd
266
267# Install Snapcast 0.34.0 from GitHub releases (requires at least 0.27)
268ARG SNAPCAST_VERSION=0.34.0
269ARG TARGETARCH
270RUN set -x \
271 && if [ "$TARGETARCH" = "arm64" ]; then \
272 SNAPCAST_ARCH="arm64"; \
273 else \
274 SNAPCAST_ARCH="amd64"; \
275 fi \
276 && wget -q "https://github.com/badaix/snapcast/releases/download/v${SNAPCAST_VERSION}/snapserver_${SNAPCAST_VERSION}-1_${SNAPCAST_ARCH}_trixie.deb" -O /tmp/snapserver.deb \
277 && wget -q "https://github.com/badaix/snapcast/releases/download/v${SNAPCAST_VERSION}/snapclient_${SNAPCAST_VERSION}-1_${SNAPCAST_ARCH}_trixie.deb" -O /tmp/snapclient.deb \
278 # install via apt so any missing dependencies of the debs are resolved as well
279 && apt-get update \
280 && apt-get install -y --no-install-recommends /tmp/snapserver.deb /tmp/snapclient.deb \
281 && apt-get clean \
282 && rm -rf /var/lib/apt/lists/* \
283 && rm /tmp/snapserver.deb /tmp/snapclient.deb
284
285# Install go-librespot (used by the Spotify Connect provider) from GitHub releases.
286# The release binaries statically link libvorbis/libogg/libFLAC, so the only dynamic
287# dependency is libasound2 (already installed above for Snapcast).
288# The pinned SHA256 per arch is verified before extraction; bump both when changing the version.
289ARG GO_LIBRESPOT_VERSION=0.9.0
290RUN set -x \
291 && if [ "$TARGETARCH" = "arm64" ]; then \
292 GO_LIBRESPOT_ARCH="arm64"; \
293 GO_LIBRESPOT_SHA256="79b80bb3723b7973165d2d94c428676b8582780aeca7c54694589206ab741e91"; \
294 else \
295 GO_LIBRESPOT_ARCH="x86_64"; \
296 GO_LIBRESPOT_SHA256="87b27ce57cc7871bad6ffac8acba7e2a89c72a7e6c7990b88bec404f449f381e"; \
297 fi \
298 && wget -q "https://github.com/devgianlu/go-librespot/releases/download/v${GO_LIBRESPOT_VERSION}/go-librespot_linux_${GO_LIBRESPOT_ARCH}.tar.gz" -O /tmp/go-librespot.tar.gz \
299 && echo "${GO_LIBRESPOT_SHA256} /tmp/go-librespot.tar.gz" | sha256sum -c - \
300 && tar -xzf /tmp/go-librespot.tar.gz -C /usr/local/bin go-librespot \
301 && chmod +x /usr/local/bin/go-librespot \
302 && rm /tmp/go-librespot.tar.gz
303
304# Copy FFmpeg binaries and libraries from builder stage
305COPY --from=ffmpeg-builder /usr/local/bin/ffmpeg /usr/local/bin/
306COPY --from=ffmpeg-builder /usr/local/bin/ffprobe /usr/local/bin/
307COPY --from=ffmpeg-builder /usr/local/lib/libav*.so* /usr/local/lib/
308COPY --from=ffmpeg-builder /usr/local/lib/libsw*.so* /usr/local/lib/
309COPY --from=ffmpeg-builder /usr/local/lib/libpostproc.so* /usr/local/lib/
310
311# Copy shairport-sync binary from builder stage (found by the airplay receiver
312# provider via its system PATH lookup)
313COPY --from=shairport-builder /usr/local/bin/shairport-sync /usr/local/bin/
314
315# Copy pre-built PyAV wheel for use by downstream images
316COPY --from=pyav-builder /wheels/ /usr/local/share/pyav-wheels/
317
318# Update shared library cache and verify FFmpeg
319RUN ldconfig && ffmpeg -version && ffprobe -version
320
321# Copy widevine client files to container
322RUN mkdir -p /usr/local/bin/widevine_cdm
323COPY widevine_cdm/* /usr/local/bin/widevine_cdm/
324
325# we need to set (very permissive) permissions to the workdir
326# and /tmp to allow running the container as non-root
327RUN chmod -R 777 /tmp
328
329LABEL \
330 org.opencontainers.image.title="Music Assistant Base Image" \
331 org.opencontainers.image.description="Base Image for Music Assistant server - not to be used directly" \
332 org.opencontainers.image.source="https://github.com/music-assistant/server" \
333 org.opencontainers.image.authors="The Music Assistant Team" \
334 org.opencontainers.image.licenses="Apache License 2.0"
335