Rename host library to master throughout

The two libraries are now "master" and "slave" everywhere: UI text, API
JSON fields (master_root, rel_master, master_size, ...), Go identifiers,
database columns, quality verdicts, quarantine subfolders and docs.

Existing databases are upgraded by a new migration that renames the
columns in place and rewrites stored "host" values, so pairs, history,
cached verdicts and restorable quarantine entries carry over. Network
"host" usages (SplitHostPort, the Origin/Host check) are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cdnutterandClaude Opus 5.5 committed 2026-09-22 16:50:55 -07:00
1 parent d06b6d546d
commit 5b088bb4a6
19 files changed
+339 -326

No files matched your search

+9 -9
View File
@@ -1,7 +1,7 @@
# playlink
Deduplicate music libraries with hardlinks. playlink finds songs that exist in both a **host** library (e.g. your full `Albums`) and a
**slave** library (e.g. a curated `Singles` library) and replaces the slave copy with a hardlink to the host copy. Each song then takes
Deduplicate music libraries with hardlinks. playlink finds songs that exist in both a **master** library (e.g. your full `Albums`) and a
**slave** library (e.g. a curated `Singles` library) and replaces the slave copy with a hardlink to the master copy. Each song then takes
up disk space only once, while both libraries keep looking exactly the same to Plex, Navidrome, Jellyfin and other media servers.
It ships as a single Go binary with an embedded React web UI and SQLite state, packaged as a small Docker image, in the style of
@@ -26,15 +26,15 @@ path on both sides. The fallbacks below catch small differences, and a scan warn
**Manual review** shows both files side by side and ranks their quality: lossless beats lossy, then bit depth and sample rate, then
codec-weighted bitrate (Opus and AAC count for more per kbps than MP3). You then have three choices:
- **Link to host copy.** The default. The host is kept because it belongs to a complete album. If the host is *lower* quality, playlink
- **Link to master copy.** The default. The master is kept because it belongs to a complete album. If the master is *lower* quality, playlink
warns you and asks for explicit confirmation.
- **Use slave copy as host.** The override. The slave's better file replaces the host file, the old host file is quarantined, and both
- **Use slave copy as master.** The override. The slave's better file replaces the master file, the old master file is quarantined, and both
libraries share the better file.
- **Ignore.** The pair is hidden from review until you un-ignore it.
**Build mode** covers the "start fresh" workflow. Point the slave at an empty folder, browse the host library, tick the
**Build mode** covers the "start fresh" workflow. Point the slave at an empty folder, browse the master library, tick the
folders or songs you want, and playlink hardlinks them in at the same relative path. **Remove** deletes slave files, but only ones that are hardlinks of the
host file.
master file.
## Safety model
@@ -52,10 +52,10 @@ playlink is built so that no user data is ever lost, even if it crashes or loses
expires, or until you purge it.
- **Every operation can be undone.** An identical link is undone by writing an independent, verified copy back. A quarantined file is
undone by restoring the original.
- **Removal is limited to hardlinks.** Build/Remove only deletes a slave file that shares an inode with the host file, so the last
- **Removal is limited to hardlinks.** Build/Remove only deletes a slave file that shares an inode with the master file, so the last
copy of any data is never deleted.
- **Paths are confined.** Libraries and the quarantine must be inside `PLAYLINK_ALLOWED_ROOTS`. Symlinks are never followed or
replaced, `..` is rejected, and host and slave may not overlap.
replaced, `..` is rejected, and master and slave may not overlap.
- **Mutations are serialized.** Only one filesystem mutation runs at a time, and only one job runs per pair.
- **Auto-link is conservative.** It only acts on files with the *exact* same path, and it skips files modified within the settle time
(default 10 minutes) so it never touches a download in progress.
@@ -83,7 +83,7 @@ services:
Then open `http://<server>:7480`, create your account, and add a pair:
- Host: `/data/Music/Albums`
- Master: `/data/Music/Albums`
- Slave: `/data/Music/Instances/cdnutter/Singles`
Run a scan, check the Review tab, and turn off dry run once the planned actions in Activity look right.