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>
23 lines
541 B
TOML
23 lines
541 B
TOML
[build-system]
|
|
requires = ["setuptools>=77"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[project]
|
|
name = "music-mirror"
|
|
version = "0.1.0"
|
|
description = "Maintain a lossy MP3 mirror of a lossless music library"
|
|
readme = "README.md"
|
|
requires-python = ">=3.11"
|
|
# No runtime Python dependencies: the work is done by ffmpeg, which must be on
|
|
# PATH.
|
|
dependencies = []
|
|
|
|
[project.scripts]
|
|
music-mirror = "music_mirror:run"
|
|
|
|
[project.urls]
|
|
Homepage = "https://code.emmathe.dev/lyrathorpe/music-mirror"
|
|
|
|
[tool.setuptools]
|
|
py-modules = ["music_mirror"]
|