Skip to content

feat(rb-dates): preserve Rekordbox "Date Added" across a lossy→lossless swap#15

Open
deviationist wants to merge 1 commit into
feat/lossless-tagsfrom
feat/rb-date-preservation
Open

feat(rb-dates): preserve Rekordbox "Date Added" across a lossy→lossless swap#15
deviationist wants to merge 1 commit into
feat/lossless-tagsfrom
feat/rb-date-preservation

Conversation

@deviationist

Copy link
Copy Markdown
Owner

Stacked on #14 (feat/lossless-tags). Review/merge that first; this PR's base will retarget to main once #14 lands. The diff here is just the rb-dates changes.

What & why

Replacing a file forces a re-add in Rekordbox (re-analyze won't do for a format change), which resets djmdContent.created_at to now and loses the original "Date Added" — the very field the dates command syncs into Plex. This adds a rb-dates subcommand that rescues it: capture the old date before the swap, restore it after you re-add the file. Keyed by file path.

Two phases

  • snapshot (read-only): resolve each lossy file to its Rekordbox row, read the raw created_at, and stash it in a sidecar JSON keyed by the lossless path (raw string → byte-exact restore). Also exposed as a lossless-tags --snapshot-dates hook so it's captured during the tag copy, before any --delete-lossy.
  • apply (read-only/dry-run by default): re-resolve the re-added lossless row by path and write the date back.

⚠️ First sanctioned write to the Rekordbox DB

Every other Rekordbox access is strictly mode=ro. The single write path (rekordbox/RekordboxDBWriter.py):

  • Opens the real master.db read-write.
  • Is USN-correct — bumps the row's rb_local_usn + the global agentRegistry.localUpdateCount, exactly as Rekordbox does for a local edit.
  • wal_checkpoint(TRUNCATE)s so the Resilio-synced file stays consistent across workstations.

It is heavily guarded (utils/rb_db_safety.py): the WRITE-RB-DATES token + a typed "free up the DB" checklist (close Rekordbox on every workstation, pause Resilio), a non-empty-WAL refusal, a file-stability probe, a local lsof check, and a mandatory master.db backup. Successfully-restored entries are pruned from the sidecar (idempotent re-runs).

Verification

Exercised end-to-end against a copy of the live encrypted DB: created_at written, global USN 341737→341738, row rb_local_usn→341738, idempotent re-apply, WAL truncated to 0 — live DB untouched. 19 new unit tests (sidecar store, safety guards, USN SQL builder, pairing); full suite 141 passing; ruff/black/mypy clean. Docs updated (CLAUDE.md, README runbook, .env.example, .gitignore).

🤖 Generated with Claude Code

…ss swap

Replacing a file forces a re-add in Rekordbox, which resets djmdContent.created_at
to now and loses the original "Date Added". New `rb-dates` subcommand rescues it via
a two-phase, path-keyed flow:

- `snapshot` (read-only): resolve each lossy file to its Rekordbox row, read the raw
  created_at, and stash it in a sidecar JSON keyed by the lossless path. Also exposed
  as a `lossless-tags --snapshot-dates` hook so capture happens in the tag-copy pass,
  before any --delete-lossy.
- `apply` (read-only/dry-run by default): re-resolve the re-added lossless row by
  path and restore the date.

This is the FIRST and only sanctioned write to the Rekordbox DB (every other access
stays mode=ro). The write (RekordboxDBWriter) opens the real master.db read-write, is
USN-correct (bumps the row's rb_local_usn + agentRegistry.localUpdateCount as a normal
local edit does), and wal_checkpoint(TRUNCATE)s so the Resilio-synced file stays
consistent. It is heavily guarded (rb_db_safety): WRITE-RB-DATES token + a "free up the
DB" checklist (close Rekordbox everywhere, pause Resilio), non-empty-WAL refusal,
file-stability probe, local lsof check, and a mandatory master.db backup.

Pairing logic extracted to utils/pairing.py and shared with lossless-tags. Path
identity bridged host→container→Rekordbox via resolve_rb_id_by_host_path. Verified
end-to-end against a copy of the live encrypted DB (created_at written, USN bumped
341737→341738, WAL truncated, idempotent re-apply). 19 new unit tests; full suite
141 passing; ruff/black/mypy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@deviationist
deviationist force-pushed the feat/rb-date-preservation branch from 13d7a61 to 890258a Compare June 11, 2026 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant