Commit Graph
3 Commits
Author SHA1 Message Date
Emma ThorpeandClaude Opus 5 7a57e03c7b fix: keep the mirror stable across Lidarr upgrades and renames
Build and publish container / build (pull_request) Canceled after 6m18s
Three defects in how the mirror tracked its source, all of which show up on a
library that something else reorganises.

Pruning probed the source tree for a mirror file's original name, trying each
known extension in turn. A source saved as .FLAC was never found, so its mirror
file was deleted as an orphan and re-encoded on the next pass, for ever.
Pruning now works from the set of paths the pass actually accounted for, which
cannot disagree with the walk over letter case or extension coverage.

Two sources could also claim one mirror path -- 01 Song.flac beside a leftover
01 Song.mp3, which is what an interrupted upgrade leaves behind. Both encoded
to the same destination, whichever finished last won the race, and every later
pass found the other one stale. The best-quality source now wins, ties break on
path, and the loser is logged.

The source mtime was read after encoding rather than before. A file still being
written when the pass reached it would be stamped with its final mtime while
holding truncated audio, and would never be revisited.

Adds regression tests for all three, plus the format-upgrade, album-rename and
whole-library-deletion cases, each verified to fail before the change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 14:59:43 +01:00
Emma ThorpeandClaude Opus 5 82c9da6d62 ci: publish the container image to the Gitea registry
Build and publish container / build (pull_request) Canceled after 14m40s
Adopts the release scheme from legacy-email-proxy so both repositories behave
the same way: the version is derived from conventional commits since the last
v* tag, the image is pushed under the full version, the truncated major.minor
and major forms, and latest, and non-release builds are published as
sha-<short>. Multi-arch (amd64 for the NAS, arm64 so the same image runs on a
Pi). Authentication uses the PACKAGES_SECRET repository secret.

The release step also writes the computed version into pyproject.toml and
commits it as chore(release) before tagging, so the packaging metadata cannot
drift behind the release. It skips the commit when the file already carries
that version, which would otherwise fail the job after the image had been
pushed.

compose.yaml and the README now reference the published image instead of
instructing the NAS to build one locally.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 14:44:29 +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