Commit Graph
3 Commits
Author SHA1 Message Date
Emma ThorpeandClaude Opus 5 81dce801ce build: base the image on Alpine to drop 387 MB of graphics stack
Build and publish container / build (pull_request) Canceled after 4m29s
Debian's ffmpeg package depends on libavdevice, which can capture from and
render to X11, Wayland, SDL and OpenGL. That pulls in Mesa, and Mesa pulls in
LLVM for its software rasteriser -- 127 MB of it -- plus Z3 at 27 MB and a
speech synthesiser at 28 MB, in an image whose only job is to encode MP3s on a
headless NAS.

Alpine's ffmpeg brings none of that. The runtime image goes from 576 MB to
189 MB. Small X11 and Wayland client libraries remain, but they are kilobytes
rather than megabytes.

The test suite passes unchanged inside the new image, which is the point of
running it there: the encoder under test is now a musl build from a different
distribution, and the tests cover exactly the tag, cover art and container
behaviour that could have differed. Verified separately that a CBR 256 encode
comes out at the expected bitrate with non-ASCII tags intact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 15:21:12 +01:00
Emma ThorpeandClaude Opus 5 cbcbf30228 build: drop the Nix packaging and test inside the container
Build and publish container / build (pull_request) Canceled after 14m59s
The deployment target is a container on TrueNAS Scale, so the flake sat on no
path between the source and the NAS. It was carried over from a sibling project
where the flake is the deployment mechanism; here it only added a second build
path and a second dependency pin.

Worse, it tested the wrong thing: `nix flake check` ran the suite against
nixpkgs' ffmpeg while the shipped artefact contains Debian's, and encoder and
muxer behaviour is exactly what these tests cover.

The Dockerfile gains a `test` stage that installs pytest and runs the suite
against the image's own ffmpeg; a failing test fails the build. CI runs
`docker build --target test` in place of the host-based Python setup, and the
push build states `target: runtime` so the published image is the lean stage
rather than the last one in the file. The runtime image carries neither the
tests nor pytest.

The release step now calls python3 rather than python, since setup-python is
no longer in the job to provide the alias.

Removes package.nix, flake.nix and flake.lock. A dev shell is a `nix shell`
away for anyone who wants one, and the README says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 15:06:06 +01:00
Emma ThorpeandClaude Opus 5 177761bb75 feat: mirror a lossless library to MP3 for iPod sync
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>
2026-08-21 14:32:28 +01:00