Apple's Music app cannot read FLAC, so getting a lossless library onto an iPod requires a converted copy somewhere. This keeps that copy beside the library on a NAS rather than on a laptop, and keeps it current unattended. Walks a source tree and reproduces it path for path as MP3: tags and cover art carried across, already-MP3 sources copied rather than re-encoded, and mirror files whose source has gone deleted along with any directories they emptied. The source library is never written to. Freshness is tracked by modification time -- an encoded file is stamped with its source's mtime, so a file is stale exactly when the two differ. That keeps runs idempotent without a database that could fall out of step with whatever owns the library, which here is Lidarr. Encodes go to a temporary file and are renamed into place, so an interrupted run cannot leave a truncated MP3 that the next run mistakes for finished work. A lock file in the mirror root prevents overlapping passes. Ships as a Python package with a console script, a container image with a TrueNAS Scale compose file, and a Nix flake providing the package, an overlay and a dev shell. The test suite runs real ffmpeg encodes rather than mocks -- the failures worth catching are in what ffmpeg does with tags, cover art and container formats. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
18 lines
522 B
Docker
18 lines
522 B
Docker
FROM python:3.13-slim
|
|
|
|
ENV PYTHONUNBUFFERED=1
|
|
|
|
# ffmpeg does the encoding; the application itself has no Python dependencies.
|
|
RUN apt-get update \
|
|
&& apt-get install --no-install-recommends -y ffmpeg \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /app
|
|
COPY pyproject.toml README.md ./
|
|
COPY music_mirror.py ./
|
|
RUN pip install --no-cache-dir .
|
|
|
|
# Runs as root by default so a bind-mounted dataset of any ownership is
|
|
# writable. Override with `user:` in compose to run as the dataset's owner.
|
|
ENTRYPOINT ["music-mirror"]
|