name: audioserver # ── Shared pipe directory ────────────────────────────────────────────────────── # FIFOs for audio routing live at AUDIO_PIPES_DIR on the host. # Run ./scripts/init-pipes.sh once before `docker compose up`. # The turntable systemd service on the host writes to turntable.fifo. x-audio: &audio-volume type: bind source: ${AUDIO_PIPES_DIR:-/opt/audiocontrol/pipes} target: /audio services: # ── Snapserver ─────────────────────────────────────────────────────────────── snapserver: build: ./snapserver container_name: snapserver restart: unless-stopped ports: - "1704:1704" # Snapcast streaming (clients connect here) - "1705:1705" # JSON-RPC WebSocket (frontend) - "1780:1780" # Snapcast HTTP API volumes: - ./snapserver/snapserver.conf:/etc/snapserver.conf:ro - <<: *audio-volume environment: - SNAPSERVER_HOST=${SNAPSERVER_HOST:-192.168.178.100} - TCP_POP_HOST=${TCP_POP_HOST:-192.168.178.95} - TCP_TP_HOST=${TCP_TP_HOST:-192.168.178.89} - TCP_FRAMEWORK_HOST=${TCP_FRAMEWORK_HOST:-192.168.178.183} # Snapserver reads FIFOs — needs to stay running even when pipes are empty # so it must open them in non-blocking mode (handled by snapserver itself) # ── Icecast2 (HTTP audio stream server) ───────────────────────────────────── icecast: build: ./icecast container_name: icecast restart: unless-stopped ports: - "8000:8000" # HTTP stream — mobile/browser clients connect here environment: - ICECAST_SOURCE_PASSWORD=${ICECAST_SOURCE_PASSWORD:-groove_source} - ICECAST_RELAY_PASSWORD=${ICECAST_RELAY_PASSWORD:-groove_relay} - ICECAST_ADMIN_PASSWORD=${ICECAST_ADMIN_PASSWORD:-groove_admin} - ICECAST_LISTENER_PASSWORD=${ICECAST_LISTENER_PASSWORD:-groove_listen} # ── Stream Relay (PCM FIFO → MP3 → Icecast) ───────────────────────────────── stream-relay: build: ./stream-relay container_name: stream-relay restart: unless-stopped volumes: - <<: *audio-volume environment: - ICECAST_HOST=icecast - ICECAST_PORT=8000 - ICECAST_SOURCE_PASSWORD=${ICECAST_SOURCE_PASSWORD:-groove_source} - ICECAST_MOUNT=/stream.flac depends_on: - icecast # ── Mopidy ─────────────────────────────────────────────────────────────────── mopidy: build: ./mopidy container_name: mopidy restart: unless-stopped # Port 6680 is only exposed to the Docker network (proxied by frontend Nginx) # Remove the comment below to also expose it on the host for debugging: # ports: # - "6680:6680" volumes: - ./mopidy/mopidy.conf:/config/mopidy.conf:ro - ${MUSIC_DIR:-/opt/audiocontrol/music}:/music:ro - mopidy_data:/var/lib/mopidy - bandcamp_config:/config/shared:ro - <<: *audio-volume depends_on: - snapserver - stream-relay environment: - TZ=${TZ:-Europe/Vienna} - SPOTIFY_CLIENT_ID=${SPOTIFY_CLIENT_ID} - SPOTIFY_CLIENT_SECRET=${SPOTIFY_CLIENT_SECRET} - SNAPSERVER_HOST=${SNAPSERVER_HOST:-192.168.178.100} - BEETS_HOST=${BEETS_HOST:-192.168.178.100} # ── Bandcamp API (credential store + login test) ───────────────────────────── bandcamp-api: build: ./bandcamp-api container_name: bandcamp-api restart: unless-stopped volumes: - bandcamp_config:/config # ── Frontend (React + Nginx proxy) ─────────────────────────────────────────── frontend: build: context: . dockerfile: ./frontend/Dockerfile args: # Pass the BeoCreate address into the Nginx config at build time BEOCREATE_HOST: ${BEOCREATE_HOST:-beocreate.local} BEOCREATE_PORT: ${BEOCREATE_PORT:-13141} environment: # Keep the generated Nginx config aligned with .env at container start. BEOCREATE_HOST: ${BEOCREATE_HOST:-beocreate.local} BEOCREATE_PORT: ${BEOCREATE_PORT:-13141} container_name: audiocontrol-frontend restart: unless-stopped extra_hosts: - "host.docker.internal:host-gateway" ports: - "${FRONTEND_PORT:-8180}:80" depends_on: - snapserver - mopidy - bandcamp-api volumes: mopidy_data: bandcamp_config: