v0.9.1: playlist merges stop losing your track order
What scrambled `a nissa one` took three defects at once. Reorder it on machine A; open it on machine B and resize the window; B's file is now newer, so the merge takes B's order — the old one — wholesale. _merge_playlist was a 2-way union with no common ancestor: one side's order wholesale, the other side's extras appended at the tail, so a track inserted in the middle on one machine arrived at the bottom on the other. merge_track_order re-inserts each side-only track after the nearest track both copies share instead. _reconcile_playlist (the live-reload path) uses the same helper. The union invariant is unchanged and property-tested over 300 random pairs — a merge never drops a track. Whose order wins is now Playlist.date_modified, bumped only in _set_track_ids (add / remove / reorder / undo) and deliberately not by mark_playlist_settings_dirty, with a file-mtime fallback for playlists written before the field existed. The file's mtime was a lie: the last column is stretch-sized, so Qt re-fires sectionResized whenever the viewport width changes, and a window resize or splitter drag rewrote the open playlist's JSON — moving its mtime and handing Syncthing another conflict — for a width apply_settings overrides on load anyway. _on_section_resized now skips that section; genuine drags on every other column still persist. Replayed on a copy of the real playlists: `a nissa one` keeps its reorder against a newer-by-mtime opponent, and `a nissa ideas` takes the other machine's two mid-list inserts at 5 and 12 rather than 26 and 27. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M8Mzze7shr5pZoEgKQU5NW
This commit is contained in:
@@ -12,28 +12,25 @@ When a round closes, move its finished items to `tasks-done.md`.
|
||||
## Round 37 (2026-08-19) — Export Playlist: done, see tasks-done.md
|
||||
|
||||
Folder + web-mix export, audio.js/jQuery dropped for a dependency-free
|
||||
player, lossless-only conversion. Round 36 below is still open.
|
||||
player, lossless-only conversion.
|
||||
|
||||
## Round 38 (2026-08-20) — merge windows + play journals: done, see tasks-done.md
|
||||
## Round 39 (2026-08-20) — playlist order + honest date_modified: done, see tasks-done.md
|
||||
|
||||
## Round 36 — the merge rework (the playlist half is still open)
|
||||
## Round 36 — the merge rework (closed out by Rounds 38 and 39)
|
||||
|
||||
The three symptoms in Round 35 shared a root cause; that round fixed the
|
||||
performance half. Round 38 took the play-count and dialog items; the two
|
||||
playlist items below are what's left.
|
||||
performance half. Round 38 took the play-count and dialog items, Round 39 the
|
||||
two playlist ones. Only the `.stignore` note below is left.
|
||||
|
||||
- [ ] **Playlist merges lose position.** `_merge_playlist` is a 2-way union with
|
||||
no common ancestor: it takes one side's order wholesale and *appends* the
|
||||
other side's extras, so a track inserted in the middle on one machine
|
||||
arrives at the tail on the other (this is what happened to `nissa one`).
|
||||
Replace with an anchor-based merge — insert each side-only track after its
|
||||
nearest preceding common anchor. Same in `_reconcile_playlist`.
|
||||
- [ ] **mtime is a lie for playlists.** `mark_playlist_settings_dirty` rewrites
|
||||
the whole playlist file for UI-only changes, and because the last column
|
||||
is stretch-sized, *resizing the window* rewrites the open playlist's JSON.
|
||||
So "most recently edited" often means "most recently resized". Give
|
||||
`Playlist` a `date_modified` bumped only in `_set_track_ids`, and merge on
|
||||
that.
|
||||
- [x] **Playlist merges lose position.** Done in Round 39 — `merge_track_order`
|
||||
anchors each side-only track to the nearest track both copies share.
|
||||
Replayed against the real `a nissa one`: the reorder survives and a
|
||||
mid-list insert lands at index 5, not 26.
|
||||
- [x] **mtime is a lie for playlists.** Done in Round 39 — `Playlist.date_modified`
|
||||
is bumped only in `_set_track_ids`, and `_on_section_resized` now ignores
|
||||
the stretched last column, so resizing the window doesn't rewrite the
|
||||
playlist file at all.
|
||||
- [x] **`max()` play counts discard concurrent plays.** Done as described —
|
||||
per-machine `plays/<machine-id>.json` journals, machine id in the config
|
||||
dir. Three plays on the real library now write 83 bytes instead of 15 MB,
|
||||
|
||||
Reference in New Issue
Block a user