v0.12.0: the merge report stops reporting things you didn't do
"unplayed", "missed and never skipped" and "top 100 in past year" showed up in the merge window constantly, and nobody had touched their rules. A live smart playlist's membership is derived, but it was being persisted — and recompute_smart_playlist rewrites it (and bumped date_modified) every time a play count moves. Both machines did that against the same file after every song, so Syncthing conflicted on a list the next load throws away and rebuilds anyway. Membership now stays in memory: save_playlist writes track_ids: [] for a live smart playlist, and the recompute passes touch=False so it marks nothing dirty and moves no timestamp. live_update=False and unsupported criteria are unchanged — their track_ids are a snapshot, which is real content. Since _mark_playlist also dirties the metadata, library_metadata.json stops being rewritten every song too. The rest was presentation. Every summary read at the same weight, so someone resizing a column on the other machine popped and raised the same window as a 21-track reconciliation, described as "Library columns/settings taken from the most recently edited copy." Summaries now carry a level — WARNING for a merge that couldn't resolve cleanly or discarded a side, CHANGE for real content, INFO for cosmetic or derived — and the dialog has a Show: selector that filters to one level and above. It opens at the highest level in the batch, so nothing routine steals focus and a blank window can't happen; a later warning always pulls the view back up to it. The wording says what happened instead of how the merge works: a metadata merge names the keys that actually differed, and adopting the other machine's music folder grades CHANGE rather than INFO, because that one is a setting somebody chose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y9ZEFi4qNJ39FMiBtiAxy2
This commit is contained in:
@@ -64,7 +64,13 @@ persistence) → GUI (Qt widgets that read the manager and connect to its signal
|
||||
`Playlist.date_modified` — bumped *only* in
|
||||
`LibraryManager._set_track_ids` — not by the file's mtime, which moves for
|
||||
cosmetic reasons. `library_manager._reconcile_playlist` uses the same helper
|
||||
for the live-reload path.
|
||||
for the live-reload path. Since Round 42 every `ConflictSummary` carries a
|
||||
**`level`** (`WARNING` / `CHANGE` / `INFO`): a merge that only reconciled
|
||||
column widths, or a smart playlist whose rules are byte-identical on both
|
||||
sides, is `INFO` and must never read as an edit the user made. The dialog
|
||||
(`gui/conflict_dialog.py`) shows one level *and above* and opens at the
|
||||
highest level in the batch, so a routine merge never steals focus but a
|
||||
blank window is impossible either.
|
||||
|
||||
- **`lintunes/storage/play_journal.py`** — why play counts can't conflict. Since
|
||||
Round 38 `library.json` holds only a **base** count and each machine owns
|
||||
@@ -233,6 +239,16 @@ persistence) → GUI (Qt widgets that read the manager and connect to its signal
|
||||
change; `track_table._on_section_resized` ignores that section, or resizing
|
||||
the window would rewrite the open playlist's JSON (and hand Syncthing a
|
||||
conflict) purely for a width `apply_settings` overrides on load anyway.
|
||||
Round 42 is the same rule one level down: a **live smart playlist's membership
|
||||
is never persisted** (`Playlist.has_derived_membership` gates it in
|
||||
`json_storage.save_playlist`, which writes `track_ids: []` +
|
||||
`derived_membership: true`) and `recompute_smart_playlist` passes
|
||||
`touch=False` to `_set_track_ids` so it marks nothing dirty and moves no
|
||||
timestamp. Membership is rebuilt from the criteria on every load
|
||||
(`recompute_all_smart`), so storing it only bought a conflict per song — one
|
||||
per finished track, on the same file, from both machines. The exceptions are
|
||||
`live_update=False` and `unsupported` criteria: their `track_ids` *are* the
|
||||
content (a snapshot), so they still persist and still count as edits.
|
||||
|
||||
- **Qt/Wayland gotchas (GNOME/Mutter):** `QDrag.setPixmap` / `setDragCursor` /
|
||||
`QCursor.pos()` are unreliable during a drag — `gui/drag_ghost.py` paints its
|
||||
|
||||
Reference in New Issue
Block a user