Commit Graph
642 Commits
Author SHA1 Message Date
yonlu e1bfe12903 fix: register playCount column in backend AllColumnIDs + rename to 'Play Count'
Backend validation rejected 'playCount' as unknown column ID, silently
preventing it from being enabled or persisted. Added ColPlayCount to
the tracklist package's constant list and AllColumnIDs slice.
2026-03-21 22:53:52 -04:00
yonlu 2db6e09aa3 fix: use sql.NullTime for last_played to handle NULL scan
COALESCE(last_played, '') returned empty string which can't scan into
time.Time. Removed COALESCE, use sql.NullTime instead. Format to string
only when Valid.
2026-03-21 15:40:34 -04:00
yonlu b643aee5a3 fix: mapTrackRow accepts time.Time for LastPlayed (matches sqlcgen Row type) 2026-03-21 15:36:59 -04:00
yonlu 2ee157a376 feat(M003/S03): play count column + data pipeline
Backend:
- GetAllTracksWithFullMetadata queries now select play_count and last_played
- mapTrackRow accepts and passes through PlayCount/LastPlayed
- PlayCount + LastPlayed added to library.Track struct
- sqlcgen Row types updated with new fields

Frontend:
- PlayCount + LastPlayed added to library.Track TypeScript model
- 'Plays' column added to track-list column definitions (60px, right-aligned, sortable)

Queries without play data (search, genre, album) pass 0/empty defaults.
2026-03-21 15:33:08 -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 9bf2bbab2e feat(M003/S01): play history tracking — schema, migration, recording hook
Migration 10:
- play_history table (audio_file_id FK, played_at DATETIME, CASCADE delete)
- play_count + last_played columns on audio_files (denormalized)
- Recreated track_metadata VIEW with play_count and last_played columns

Play recording:
- queue.recordPlay() inserts play_history row + updates denormalized columns
- Called from OnPlaybackFinished after queue advance completes
- Mutex released before DB write to avoid MaxOpenConns(1) deadlock
- Natural finish only — skip/stop does not count

Tests:
- TestMigration10PlayHistory: schema, columns, VIEW, round-trip verification
- All 49 smart playlist + 15 service + existing DB tests still pass
2026-03-21 15:23:18 -04:00
yonlu ebaa856e0c fix: align combobox height to operator select via grid stretch
- Grid rows use align-items: stretch so all cells share the select's height
- Combobox host, wrapper, and input all set height: 100% to fill the cell
- Remove button uses align-self: center to stay centered instead of stretching
2026-03-21 13:46:26 -04:00
yonlu 288215a97c fix: match combobox input height to select dropdowns 2026-03-21 13:40:07 -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
yonlu e974bd2a22 Merge remote-tracking branch 'origin/main' into wip 2026-03-20 14:58:19 -04:00
yonlu f16157a213 fix(S21/T01): fix all lint warnings and upgrade wsl to wsl_v5
Files:
- .golangci.yml
- backend/events/cmd/genevents/main.go
- backend/fileutil/atomicwrite_test.go
- backend/library/library.go
- backend/player/buffered_streamer_test.go
- backend/player/player.go
- backend/tagwriter/dbsync.go
- backend/tagwriter/mp3_test.go
- backend/tagwriter/ogg.go
- backend/tagwriter/ogg_test.go
- backend/tagwriter/ogg_vorbis.go
- backend/tagwriter/pipeline.go
- backend/tagwriter/tagwriter.go
- backend/tagwriter/wav_test.go
2026-03-20 14:36:18 -04:00
yonlu 2409b3435a docs(phase-20): complete phase execution 2026-03-19 14:07:56 -04:00
yonlu 712fa260dd docs(20-02): complete OGG Vorbis tag writer tests plan 2026-03-19 14:04:52 -04:00
yonlu f13aa7086b test(20-02): add round-trip tests for all OGG requirements
- TestWriteOggTags_TextFields: all 9 text fields round-trip via dhowden/tag (OGG-01)
- TestWriteOggTags_CoverArt: METADATA_BLOCK_PICTURE embed verified (OGG-04)
- TestWriteOggTags_ClearCoverArt: cover art clear via nil (OGG-04)
- TestWriteOggTags_PartialUpdate: non-edited fields preserved (OGG-02)
- TestWriteOggTags_AudioPreservation: audio page data byte-identical (OGG-03)
- TestWriteOggTags_AtomicSafety: corrupt file untouched on failure (OGG-05)
- TestWriteOggTags_RejectNonVorbis: Theora OGG rejected (OGG-03 error path)
- TestWriteOggTags_RejectMultiStream: multi-serial rejected
2026-03-19 14:02:02 -04:00
yonlu 246a991c75 test(20-02): add OGG test fixture builder and CRC32 validation
- createTestOGG builds minimal valid OGG Vorbis file programmatically
- TestOggCRC_KnownVectors validates CRC32 against known vectors and fixture pages
- TestCreateTestOGG_Valid verifies fixture structure and metadata.ExtractTags compatibility
2026-03-19 14:00:47 -04:00
yonlu d289390396 docs(20-01): complete OGG Vorbis tag writer implementation plan
- SUMMARY.md with implementation details and decisions
- STATE.md updated with position, metrics, and key decisions
- ROADMAP.md updated with Phase 20 progress (1/2 plans)
- REQUIREMENTS.md: OGG-01 through OGG-05 marked complete
2026-03-19 13:55:39 -04:00
yonlu 5e98c03634 feat(20-01): implement OGG Vorbis tag writer with custom page parser and CRC32
- Custom OGG page parser/writer with MSB-first CRC32 lookup table (ogg.go)
- Vorbis Comment packet parse/serialize with raw byte preservation (ogg_vorbis.go)
- METADATA_BLOCK_PICTURE base64 encoding for cover art with legacy field stripping
- Multi-stream and non-Vorbis OGG rejection with clear error messages
- Pipeline integration: FormatOGG constant, .ogg in DetectFormat, writeOggTags dispatch
- Lenient-read/strict-write: warn on CRC mismatch, always write correct CRCs
- Page sequence renumbering and crash-safe writes via AtomicWrite
2026-03-19 13:53:36 -04:00
yonlu 3face33a57 docs(20): create phase plan for OGG Vorbis tag writer 2026-03-19 13:20:16 -04:00
yonlu 676eede51e docs(20): research phase domain 2026-03-19 13:15:12 -04:00
yonlu 707d759ced docs(20): capture phase context 2026-03-19 13:06:07 -04:00
yonlu 1f0fe2cc2e docs(phase-19): complete phase execution 2026-03-19 09:03:54 -04:00
yonlu f7077b15df docs(19-02): complete WAV tag writer tests plan
- SUMMARY.md with 7 test results and 2 deviations documented
- STATE.md: Phase 19 complete, all WAV requirements verified
- ROADMAP.md: Phase 19 marked complete
- REQUIREMENTS.md: WAV-06 marked complete
2026-03-19 09:00:29 -04:00
yonlu f11b523afb style(19-02): fix wsl lint warnings in WAV test and writer
- Add blank lines before cuddled expressions in wav_test.go
- Fix cuddled copy expression in wav.go writeRIFF
- All WAV lint issues resolved (remaining are pre-existing in other files)
2026-03-19 08:57:57 -04:00
yonlu 1b28882af4 test(19-02): add round-trip tests for all WAV tag writer requirements
- TestWriteWavTags_TextFields: 9 field round-trip (WAV-01)
- TestWriteWavTags_CoverArt: JPEG embed/read-back (WAV-04)
- TestWriteWavTags_ClearCoverArt: embed then clear (WAV-04)
- TestWriteWavTags_PartialUpdate: 2-of-9 change, 7 preserved (WAV-01)
- TestWriteWavTags_ChunkPreservation: fmt/data/LIST/bext preserved (WAV-02, WAV-03)
- TestWriteWavTags_AtomicSafety: failed write leaves file untouched (WAV-05)
- TestWriteWavTags_RejectsRF64: RF64 rejected with clear error (WAV-02)
- readWavID3Tags uses bogem/id3v2 ParseReader for reliable read-back
2026-03-19 08:53:36 -04:00
yonlu 21fe17212d test(19-02): add WAV test fixture builder and read-back helper
- createTestWAV: builds minimal valid WAV with optional ID3v2 tag
- readWavID3Tags: extracts ID3v2 from RIFF via parseRIFF + dhowden/tag
- createTestWAVWithExtraChunks: adds LIST INFO and bext chunks for preservation tests
- makePCMFmtData: generates 16-byte PCM format data
2026-03-19 08:50:20 -04:00
yonlu 8794584b97 docs(19-01): complete WAV tag writer implementation plan
- SUMMARY.md with task commits, deviations, decisions
- STATE.md updated with position, metrics, decisions
- ROADMAP.md updated with phase 19 progress (1/2 plans)
- REQUIREMENTS.md: WAV-01 through WAV-05 marked complete
2026-03-19 08:47:04 -04:00
yonlu e6610ff15e feat(19-01): implement WAV RIFF parser/writer and writeWavTags
- Add FormatWAV constant and .wav DetectFormat case
- Add FormatWAV dispatch in pipeline WriteTrackTags switch
- Create wav.go with custom RIFF chunk parser/writer
- parseRIFF: lenient read, RF64 rejection, case-insensitive ID3 chunk detection
- writeRIFF: strict write with correct padding and 4GB size check
- writeWavTags: merge existing ID3v2, reuse applyTextChanges/applyCoverArtChanges
- Atomic write via fileutil.AtomicWrite for crash safety
2026-03-19 08:44:38 -04:00
yonlu 8f4c4a0c2b fix(19-01): add album_artist TPE2 mapping to applyTextChanges
- Map FieldAlbumArtist to TPE2 frame via CommonID
- Follows same pattern as FieldComposer → TCOM
- Fixes latent gap in MP3 writing, enables WAV reuse
2026-03-19 08:42:53 -04:00
yonlu d789c0b68e docs(19): create phase plan for WAV tag writer 2026-03-18 20:37:24 -04:00
yonlu c5184fed79 docs(19): research phase domain 2026-03-18 20:33:37 -04:00
yonlu 75e49a839e docs(19): capture phase context 2026-03-18 19:56:59 -04:00
yonlu 5d37eb3a2a docs: create milestone v1.2.1 roadmap (3 phases) 2026-03-18 16:30:15 -04:00
yonlu 6b4adf8690 docs: define milestone v1.2.1 requirements 2026-03-18 15:01:12 -04:00
yonlu 279501f972 docs: complete project research for v1.2.1 Format Parity 2026-03-18 14:33:42 -04:00
yonlu 664de001ed docs: start milestone v1.2.1 Format Parity 2026-03-18 14:20:55 -04:00
yonlu 2256f8f329 chore: complete v1.2 Tag Editing milestone
Archive v1.2 milestone: ROADMAP + REQUIREMENTS + phases to milestones/.
Evolve PROJECT.md with v1.2 validated requirements and key decisions.
Update RETROSPECTIVE.md with v1.2 lessons and cross-milestone trends.
Clean STATE.md for next milestone.
2026-03-18 14:07:28 -04:00
yonlu e37535b115 docs(phase-18): complete phase execution 2026-03-18 13:57:00 -04:00
yonlu 77dec0bceb docs(18-02): complete batch edit UI plan
- SUMMARY.md with task commits, deviations, and self-check
- STATE.md updated: Phase 18 complete, all BATCH requirements fulfilled
- ROADMAP.md updated: 2/2 plans complete for Phase 18
- REQUIREMENTS.md updated: BATCH-02 and BATCH-04 marked complete
2026-03-18 13:53:53 -04:00
yonlu d430ad884b fix(18-02): add field labels to all track-details states (single/batch, read/edit) 2026-03-18 13:40:11 -04:00
yonlu 9df2d6764a fix(18-02): add field labels above title/artist/album inputs in batch edit mode 2026-03-18 13:32:08 -04:00
yonlu 5f21804b20 docs(18-02): update STATE.md for checkpoint pause at task 3 2026-03-18 13:14:53 -04:00
yonlu 656985add9 feat(18-02): wire batch track-details to all 4 view context menus
- track-list: branch on selection count, resolve tracks from this.tracks
- cover-grid: branch on selection count, resolve from expandedTracks
- queue-panel: branch on indices count, resolve via libraryStore.getCachedTracks
- playlist-details: branch on selection count, resolve via libraryStore
- Each view determines coverArt/coverArtMixed state and calls showBatch()
2026-03-18 13:14:02 -04:00
yonlu 6dab32b36b feat(18-02): add batch edit mode to track-details component
- showBatch() API for multi-track entry with merged field values
- Three-state field model: keep/set/clear via dirty tracking in editValues
- Confirmation dialog showing field changes before batch save
- Progress bar with live counter wired to BatchWriteProgress events
- Cancel button calling CancelBatchWrite during batch write
- Results view with success/failure counts and expandable failure details
- Batch cover art: pick, preview, or clear for all selected tracks
- Post-save data refresh returning to updated summary view
- Single-track show() path unchanged (batchMode = false)
2026-03-18 13:11:36 -04:00
yonlu 92f6353fa6 docs(18-01): complete batch write backend plan
- SUMMARY.md with BatchWriteTrackTags implementation details
- STATE.md updated with Phase 18 position and decisions
- ROADMAP.md progress updated (1/2 plans)
- REQUIREMENTS.md: BATCH-01, BATCH-03 marked complete
2026-03-18 13:04:54 -04:00
yonlu f557ffd652 feat(18-01): add BatchWriteTrackTags with progress, cancellation, and partial failure
- Add BatchFailure and BatchResult types for structured batch outcomes
- Add cancelBatch channel and suppressEvents flag to TagWriter struct
- Add CancelBatchWrite method for mid-batch cancellation from frontend
- Add BatchWriteTrackTags method processing tracks sequentially
- Emit BatchWriteProgress event per-track with current/total/succeeded/failed
- Suppress per-track TrackMetadataChanged; emit single event after batch
- Wails bindings auto-generated for BatchWriteTrackTags and CancelBatchWrite
- tagwriter namespace with BatchResult/BatchFailure in models.ts
2026-03-18 13:02:30 -04:00
yonlu 3dba0e143c feat(18-01): add BatchWriteProgress event constant
- Add BatchWriteProgress to tag writing events const block in events.go
- Regenerate frontend/src/events.ts via genevents codegen tool
2026-03-18 13:00:05 -04:00
yonlu 78f0d89331 docs(18): create phase plan — 2 plans in 2 waves for batch edit 2026-03-18 12:33:27 -04:00
yonlu 33ee843d77 docs(18): create phase plan 2026-03-18 12:30:36 -04:00
yonlu 255b8db375 docs(18): capture phase context 2026-03-18 11:44:31 -04:00