Commit Graph
2 Commits
Author SHA1 Message Date
travandClaude Opus 5 211cb50a9e v0.7.0: startup speed — the 21k-track library stops freezing
Three reported symptoms, one shape: every operation was whole-library,
whole-file, on the Qt main thread. Measured on the real library (21,482
tracks, 506 playlists, 15 MB library.json).

The track table sorted through a QSortFilterProxyModel, which asks data()
for a value on every comparison — 580k Python round trips, 8.5 s per table
load, paid again on every reload. TrackTableModel now keeps _tracks in
canonical (playlist) order plus an _order index list and sorts a key list
computed once per track. Nothing used the proxy's filtering. Sorting by #
is the identity order, so "source row" still means "playlist position" for
drag-reorder. 8.5 s -> 0.13 s; MainWindow() 18.1 s -> 0.79 s.

Smart rules compile to closures once per evaluate() instead of being
re-dispatched per track — the operator lookup, casefolding the query and
parsing the rule's own date constants all leave the 21k-iteration loop
(_match_date was re-parsing its own constant 21,482 times per rule).
Verified identical membership against the old evaluator on all 20 real
smart playlists. 2.54 s -> 0.72 s, and it now runs after the first paint.

Also: _reconcile_track skips two to_dict() round trips per unchanged track
(MERGEABLE_FIELDS in the resolver is the single source of truth for what a
merge touches); no git subprocess on the startup path (Updater.enabled is
probed lazily on the background thread, version-button tooltip deferred);
and .resolved/ is pruned to the newest 10 snapshots — it had reached 1.7 GB
across 73 snapshots inside the Syncthing share.

Time to interactive window ~15 s -> 3.2 s. The mid-session freeze when
Syncthing delivers a change — a full reload + recompute + table rebuild
with the window already on screen, which is what GNOME was offering to
force-quit — ~16 s -> 3.8 s.

The merge rework itself (playlist ordering, per-machine play journal,
quieting the dialog) is queued in TASKS.md as Round 36.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 17:09:07 -04:00
travandClaude Fable 5 502a6106f4 v0.1.0: version in the status bar + one-click self-update via git pull
__version__ in lintunes/__init__.py (setup.py regex-reads it) shows
bottom-left via the new VersionButton; the tooltip carries the git short
hash so machines on the same version but different commits are
distinguishable. The new Updater fetches upstream 10s after launch and
every 4h on daemon threads (lastfm.py pattern); commits behind put a *
on the button, and clicking it confirms, runs `git pull --ff-only`,
quits cleanly (library flush + player shutdown), and re-execs
`python -m lintunes.main` on the new code. Positional file args are
stripped from the re-exec so they don't re-import as duplicates. Pull
failures surface the git error: line in the status bar and leave the
running app untouched; outside a git checkout the button is a plain
label.

Also fixes a Round-5 regression found while verifying: the totals label
was a permanent status-bar widget with stretch=1, which squeezed the
transient-message area to zero width — every showMessage (scrobbles,
tag-write errors, import status) has been invisible since. Both
readouts are now non-permanent widgets, so a transient message
temporarily replaces them and they return.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 11:30:37 -04:00