Commit Graph
5 Commits
Author SHA1 Message Date
yonluandClaude Opus 4.8 65048401e8 feat: autotag scoring overhaul, dump-based explore index, and lyrics search
Consolidates in-progress work across autotag, explore, and library:

- autotag: beets/Picard-informed scoring engine — ID-first matching, VA
  handling, recommendation tiers, and a merged distance/rank cascade, with
  an eval harness for regression tracking.
- explore: offline MusicBrainz dump import/incremental refresh replaces the
  legacy tier crawl; index-first local search with fuzzy matching and a
  dedicated ranker; disk-free guards for dump downloads.
- library: artist-credit extraction and matching.
- lyrics: owned-library lyric search (FTS) with LRCLIB backfill.

Also: rewrite README to be user-focused, and migrate upstream to
git.ljones.me/yonlu/yellowjacket.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 12:14:20 -04:00
yonluandClaude Opus 4.6 ca574a60fe perf(smartplaylist): batch-load genres instead of per-row correlated subquery
Evaluate now issues a lean main SELECT over the joined metadata tables
with no genre column, then batch-fetches genres with a single query
using WHERE recording_id IN (...). Previously the track_metadata view's
correlated GROUP_CONCAT subquery ran per row and scaled with library
size rather than result size, producing multi-second load times for
100-track smart playlists.

- Inline the metadata joins instead of using the track_metadata view,
  so the per-row GROUP_CONCAT never runs on the hot path. Other
  callers of the view (search, library listing) are unaffected.
- Route all genre operators (is/is_not/is_any_of/contains/etc.)
  through a recording_genres subquery against af.recording_id.
  Previously text operators like "contains" matched against the
  view's concatenated genre column, which is no longer in scope.
- Sort-by-genre falls back to Go-side sort after the batch genre
  merge since there is no single SQL column to sort on.
- Log main_ms / genres_ms / total_ms at Debug for future tuning.
- Add (*DB).Logger() accessor so smartplaylist can reuse the DB's
  structured logger without changing Evaluate's signature.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:36:55 -04:00
yonlu 9c85cfcc7b feat(M003/S02): smart playlist integration — play_count and days_since_played fields
Backend:
- Added play_count and days_since_played to rule engine field whitelist
- days_since_played uses julianday() expression with COALESCE for NULL handling
- Never-played tracks (NULL last_played) match 'greater_than' but not 'less_than'
- Added PlayCount + LastPlayed to library.Track struct
- Evaluate query selects play_count and last_played from track_metadata
- Added play_count to sort field options

Frontend:
- Added play_count and days_since_played to field and numeric field lists
- Added play_count to sort options

All 49 rule engine + 15 service tests pass unchanged.
2026-03-21 15:26:43 -04:00
yonlu 6972953649 fix: smart playlist UX polish — layout, defaults, free-form input, case-insensitive matching
Details view:
- Skip evaluation on new playlists (was returning all 25k tracks)
- Go straight to editor on auto-edit instead of awaiting loadTracks

Editor:
- Default sort to Random instead of None, remove None option
- Hide sort direction button when sort is Random
- Fixed-width field (160px) and operator (140px) columns, value fills remaining space
- Preview fills remaining vertical space (flex layout) instead of fixed 200px max-height
- Preview scrolls independently while rules/options stay pinned

Combobox:
- Accept free-form text on blur and Enter — no longer requires selection from dropdown
- Enables typing values like 'indie' that may not be exact DB entries

Backend:
- Case-insensitive text matching: COLLATE NOCASE on is/is_not/is_any_of operators
- Applies to both regular fields and genre subqueries
- LIKE operators were already case-insensitive (SQLite default)
2026-03-21 13:25:21 -04:00
yonlu 477b7ff6a2 feat(M002): smart playlists — rule engine, editor UI, sidebar integration
Recovered from orphaned worktree commits (complete-milestone failed to merge).

Backend:
- Migration 9: is_smart + smart_rules columns on playlists table
- smartplaylist package: parameterized WHERE clause builder, field whitelist, genre subquery
- playlist.Service: Create/Update/Evaluate/Preview/GetRules smart playlist methods
- 65 tests (49 rule engine + 15 service + 1 migration)

Frontend:
- yj-combobox: reusable typeable dropdown with keyboard nav, ARIA, blur-race fix
- smart-playlist-editor: row-based rule builder with live preview
- smart-playlist-details: evaluate, refresh, play, shuffle, edit rules
- Sidebar: filter icon, Smart badge, create button, routing
- Queue snapshot on play/shuffle
2026-03-21 12:53:00 -04:00