docs(16-01): complete tagwriter foundation + MP3 writer plan
- Create 16-01-SUMMARY.md with execution results - Update STATE.md with Plan 01 progress and decisions - Update ROADMAP.md plan progress (2/3 plans complete) - Mark WRITE-01 requirement complete
This commit is contained in:
@@ -14,7 +14,7 @@ Requirements for v1.2 Tag Editing milestone. Each maps to roadmap phases.
|
||||
|
||||
### Tag Writing
|
||||
|
||||
- [ ] **WRITE-01**: Write metadata tags to MP3 files via ID3v2 (title, artist, album, genre, year, track#, disc#, composer)
|
||||
- [x] **WRITE-01**: Write metadata tags to MP3 files via ID3v2 (title, artist, album, genre, year, track#, disc#, composer)
|
||||
- [x] **WRITE-02**: Write metadata tags to FLAC files via Vorbis Comments
|
||||
- [ ] **WRITE-03**: Write metadata tags to OGG Vorbis files via custom page rewriter
|
||||
- [x] **WRITE-04**: Embed cover art image (JPEG/PNG) in MP3 and FLAC files
|
||||
@@ -93,7 +93,7 @@ Which phases cover which requirements. Updated during roadmap creation.
|
||||
|-------------|-------|--------|
|
||||
| SCHEMA-01 | Phase 15 | Complete |
|
||||
| SCHEMA-02 | Phase 15 | Complete |
|
||||
| WRITE-01 | Phase 16 | Pending |
|
||||
| WRITE-01 | Phase 16 | Complete |
|
||||
| WRITE-02 | Phase 16 | Complete |
|
||||
| WRITE-03 | Phase 19 | Pending |
|
||||
| WRITE-04 | Phase 16 | Complete |
|
||||
|
||||
@@ -72,7 +72,7 @@ Plans:
|
||||
3. Cover art images (JPEG/PNG) can be embedded in both MP3 and FLAC files — the embedded image is readable back and the existing cover art pipeline (extraction, thumbnails) works with the newly embedded art
|
||||
4. After a tag write, the database reflects the new metadata within the same operation: artist/album/genre entities are created or relinked (never mutated in-place), orphaned entities with zero remaining references are cleaned up, and the FTS5 index is updated — no library rescan needed
|
||||
5. If the currently-playing track is being edited, playback is stopped before the file write begins — the user does not experience a crash or corrupted audio stream
|
||||
**Plans:** 1/3 plans executed
|
||||
**Plans:** 2/3 plans executed
|
||||
Plans:
|
||||
- [ ] 16-01-PLAN.md — Tagwriter foundation + sqlc queries + MP3 writer (Wave 1)
|
||||
- [ ] 16-02-PLAN.md — FLAC writer with go-flac ecosystem (Wave 1)
|
||||
@@ -129,7 +129,7 @@ Plans:
|
||||
| 13. Library Views & Phantom Tracks | v1.1 | 2/2 | Complete | 2026-03-16 |
|
||||
| 14. Performance Optimization | v1.1 | 4/4 | Complete | 2026-03-15 |
|
||||
| 15. Schema Migration & Write Safety | 2/2 | Complete | 2026-03-16 | - |
|
||||
| 16. Tag Writing & Database Sync | 1/3 | In Progress| | - |
|
||||
| 16. Tag Writing & Database Sync | 2/3 | In Progress| | - |
|
||||
| 17. Single Track Edit | v1.2 | 0/? | Not started | - |
|
||||
| 18. Batch Edit | v1.2 | 0/? | Not started | - |
|
||||
| 19. OGG Vorbis Tag Writing | v1.2 | 0/? | Not started | - |
|
||||
|
||||
+13
-10
@@ -3,12 +3,12 @@ gsd_state_version: 1.0
|
||||
milestone: v1.2
|
||||
milestone_name: Tag Editing
|
||||
status: unknown
|
||||
last_updated: "2026-03-16T22:21:06.448Z"
|
||||
last_updated: "2026-03-17T14:42:46.662Z"
|
||||
progress:
|
||||
total_phases: 1
|
||||
total_phases: 2
|
||||
completed_phases: 1
|
||||
total_plans: 2
|
||||
completed_plans: 2
|
||||
total_plans: 5
|
||||
completed_plans: 4
|
||||
---
|
||||
|
||||
# YellowJacket — Project State
|
||||
@@ -23,9 +23,9 @@ See: .planning/PROJECT.md (updated 2026-03-16)
|
||||
## Current Position
|
||||
|
||||
Phase: Phase 16 — Tag Writing & Database Sync (in progress)
|
||||
Plan: 2 of 3 in progress
|
||||
Status: Executing Phase 16 plans — Plan 02 (FLAC writer) complete
|
||||
Last activity: 2026-03-17 — Completed 16-02 (FLAC tag writer)
|
||||
Plan: 2 of 3 complete
|
||||
Status: Executing Phase 16 plans — Plans 01 (MP3 writer + sqlc) and 02 (FLAC writer) complete
|
||||
Last activity: 2026-03-17 — Completed 16-01 (MP3 writer + orphan queries)
|
||||
|
||||
### Phase Overview
|
||||
|
||||
@@ -59,6 +59,7 @@ Last activity: 2026-03-17 — Completed 16-02 (FLAC tag writer)
|
||||
|-------|------|----------|-------|-------|
|
||||
| 15 | 01 | 15min | 2 | 5 |
|
||||
| 15 | 02 | 16min | 2 | 2 |
|
||||
| 16 | 01 | 28min | 2 | 14 |
|
||||
| 16 | 02 | 20min | 2 | 6 |
|
||||
|
||||
## Accumulated Context
|
||||
@@ -85,6 +86,8 @@ Decisions from v1.0 and v1.1 are archived in PROJECT.md Key Decisions table. Key
|
||||
| `*slog.Logger` as first param for AtomicWrite | Matches codebase convention — all packages accept logger as first arg |
|
||||
| go-flac `WriteTo(io.Writer)` for AtomicWrite integration | Pipes directly into temp file callback; avoids `Save(path)` file path conflicts |
|
||||
| `replaceVorbisComment` as filter+add pattern | flacvorbis has no Set/Replace — must remove existing entries then Add new value |
|
||||
| id3v2 WriteTo + manual audio copy for AtomicWrite | `tag.Save()` writes to original file; use `WriteTo(tmp)` + seek past tag + `io.Copy` audio data |
|
||||
| Snapshot tag size before `id3v2.Open()` | `originalSize` is unexported; read 10-byte ID3v2 header and decode synchsafe size ourselves |
|
||||
|
||||
### v1.2 Roadmap Decisions
|
||||
|
||||
@@ -125,8 +128,8 @@ Decisions from v1.0 and v1.1 are archived in PROJECT.md Key Decisions table. Key
|
||||
### Last Session
|
||||
|
||||
**Date:** 2026-03-17
|
||||
**What happened:** Executed Phase 16 Plan 02 — FLAC tag writer using go-flac ecosystem with Vorbis Comments, PICTURE blocks, AtomicWrite integration, and 7 round-trip tests.
|
||||
**Where we stopped:** Completed 16-02-PLAN.md — Plan 03 remaining
|
||||
**What happened:** Executed Phase 16 Plan 01 — MP3 tag writer with n10v/id3v2, orphan-counting sqlc queries, and 5 round-trip tests. Plans 01 and 02 now both complete.
|
||||
**Where we stopped:** Completed 16-01-PLAN.md — Plan 03 remaining
|
||||
**Next action:** Execute 16-03-PLAN.md (WriteTrackTags entry point + DB sync)
|
||||
|
||||
---
|
||||
@@ -141,4 +144,4 @@ Decisions from v1.0 and v1.1 are archived in PROJECT.md Key Decisions table. Key
|
||||
| 19 | fix phantom playlist tracks with multi-root path resolution | 2026-03-16 | 9144ded | [19-fix-phantom-playlist-tracks](./quick/19-fix-phantom-playlist-tracks/) |
|
||||
|
||||
Last activity: 2026-03-16 - Completed quick task 19: fix phantom playlist tracks with multi-root path resolution
|
||||
*Last updated: 2026-03-17 — Completed 16-02 (FLAC tag writer) — Phase 16 in progress*
|
||||
*Last updated: 2026-03-17 — Completed 16-01 (MP3 writer + orphan queries) — Phase 16 in progress (2/3)*
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
phase: 16-tag-writing-database-sync
|
||||
plan: 01
|
||||
subsystem: database, tagwriter
|
||||
tags: [sqlc, id3v2, mp3, atomicwrite, tag-writing]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 15-schema-migration-write-safety
|
||||
provides: AtomicWrite utility for crash-safe file writes
|
||||
provides:
|
||||
- TagChanges type and field name constants for diff-map API
|
||||
- writeMp3Tags function with ID3v2 + AtomicWrite integration
|
||||
- Orphan-counting sqlc queries (CountArtistCreditReferences, CountReleaseGroupRecordings, CountGenreReferences, DeleteGenre)
|
||||
- detectMIME helper for JPEG/PNG magic byte detection
|
||||
- id3v2OriginalTagSize helper for locating audio data offset in MP3 files
|
||||
affects: [16-tag-writing-database-sync, 17-single-track-edit]
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: [github.com/bogem/id3v2/v2]
|
||||
patterns: [diff-map tag changes, synchsafe integer decoding, ID3v2 WriteTo + audio copy for atomic rewrite]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- backend/tagwriter/tagwriter.go
|
||||
- backend/tagwriter/mp3.go
|
||||
- backend/tagwriter/mp3_test.go
|
||||
modified:
|
||||
- backend/database/sql/queries/recordings.sql
|
||||
- backend/database/sql/queries/artist_credit.sql
|
||||
- backend/database/sql/queries/release_groups.sql
|
||||
- backend/database/sql/queries/genres.sql
|
||||
- backend/database/sql/sqlcgen/recordings.sql.go
|
||||
- backend/database/sql/sqlcgen/artist_credit.sql.go
|
||||
- backend/database/sql/sqlcgen/release_groups.sql.go
|
||||
- backend/database/sql/sqlcgen/genres.sql.go
|
||||
- go.mod
|
||||
- go.sum
|
||||
|
||||
key-decisions:
|
||||
- "Used id3v2.WriteTo + manual audio data copy for AtomicWrite integration instead of tag.Save()"
|
||||
- "Snapshot original tag size before opening for edit to reliably locate audio data offset"
|
||||
- "Shared test helpers in helpers_test.go for both MP3 and FLAC test files"
|
||||
|
||||
patterns-established:
|
||||
- "id3v2 WriteTo + copyAudioData pattern: write new tag to temp file, seek past original tag in source, copy audio data, atomic rename"
|
||||
- "Synchsafe integer decoding for ID3v2 header parsing"
|
||||
|
||||
requirements-completed: [WRITE-01, WRITE-04]
|
||||
|
||||
# Metrics
|
||||
duration: 28min
|
||||
completed: 2026-03-17
|
||||
---
|
||||
|
||||
# Phase 16 Plan 01: Tagwriter Foundation + MP3 Writer Summary
|
||||
|
||||
**MP3 tag writer with n10v/id3v2 using AtomicWrite for crash-safe ID3v2 rewriting, plus orphan-counting sqlc queries for entity cleanup**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 28 min
|
||||
- **Started:** 2026-03-17T14:12:10Z
|
||||
- **Completed:** 2026-03-17T14:40:39Z
|
||||
- **Tasks:** 2
|
||||
- **Files modified:** 14
|
||||
|
||||
## Accomplishments
|
||||
- Orphan-counting sqlc queries for artist credits, release groups, and genres (5 new queries across 4 SQL files)
|
||||
- MP3 tag writing for all 8 text fields + cover art embed/clear via n10v/id3v2 with AtomicWrite crash safety
|
||||
- Round-trip tests verify tags written by id3v2 are readable by dhowden/tag (metadata.ExtractTags)
|
||||
- TagChanges diff-map type and field constants established as the public API for callers
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically:
|
||||
|
||||
1. **Task 1: Add orphan-counting sqlc queries** - `3642cbe` (feat) — queries were included in an earlier commit alongside tagwriter foundation
|
||||
2. **Task 2: Create tagwriter package with types and MP3 writer** - `6bd65a6` (feat) — mp3.go and mp3_test.go with 5 round-trip tests
|
||||
|
||||
**Plan metadata:** (this commit)
|
||||
|
||||
_Note: Tasks 1 and 2 were committed by a concurrent session that also executed Plan 02 (FLAC writer). The sqlc queries and tagwriter.go were committed in `3642cbe` alongside FLAC work; the MP3 writer was committed in `6bd65a6` alongside Plan 02's summary._
|
||||
|
||||
## Files Created/Modified
|
||||
- `backend/tagwriter/tagwriter.go` — Package types (TagChanges, AudioFormat), field constants, format detection, MIME detection, ID3v2 tag size helper
|
||||
- `backend/tagwriter/mp3.go` — writeMp3Tags with applyTextChanges, applyCoverArtChanges, copyAudioData
|
||||
- `backend/tagwriter/mp3_test.go` — 5 tests: text fields, cover art, clear art, partial update, atomic safety
|
||||
- `backend/tagwriter/helpers_test.go` — Shared test helpers (testLogger, tinyJPEG, assertEqual, assertStrField, assertIntField)
|
||||
- `backend/database/sql/queries/recordings.sql` — CountRecordingsByArtistCredit query
|
||||
- `backend/database/sql/queries/artist_credit.sql` — CountArtistCreditReferences query
|
||||
- `backend/database/sql/queries/release_groups.sql` — CountReleaseGroupRecordings query
|
||||
- `backend/database/sql/queries/genres.sql` — CountGenreReferences and DeleteGenre queries
|
||||
- `go.mod` / `go.sum` — Added github.com/bogem/id3v2/v2 v2.1.4
|
||||
|
||||
## Decisions Made
|
||||
- **id3v2 WriteTo + manual audio copy** — The n10v/id3v2 library's `Save()` method writes directly to the original file, bypassing AtomicWrite. Instead, we use `WriteTo(tmpFile)` to write the new tag, then read the original file's audio data (skipping past the original ID3v2 header using `id3v2OriginalTagSize()`) and copy it into the temp file. AtomicWrite renames the temp file over the original.
|
||||
- **Snapshot tag size before Open** — `id3v2.Tag.originalSize` is unexported. We read the ID3v2 header ourselves (10-byte header with synchsafe size integer) to determine where audio data starts. This is done before `id3v2.Open()` to avoid any interference.
|
||||
- **Shared test helpers across formats** — Created `helpers_test.go` with common test utilities (logger, JPEG generator, assertion functions) used by both mp3_test.go and flac_test.go.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] Pre-existing untracked FLAC writer files from aborted session**
|
||||
- **Found during:** Task 2 (MP3 writer implementation)
|
||||
- **Issue:** The `backend/tagwriter/` directory already contained `tagwriter.go`, `flac.go`, `flac_test.go`, and `helpers_test.go` from a previous aborted session that had executed Plan 02 before Plan 01. These files were untracked but present on disk, causing compilation conflicts.
|
||||
- **Fix:** Integrated with the existing file layout — used helpers from `helpers_test.go` instead of duplicating, and ensured `mp3.go` fit into the existing package structure.
|
||||
- **Files modified:** mp3_test.go (adapted to use existing shared helpers)
|
||||
- **Verification:** All 12 tests pass, lint clean
|
||||
- **Committed in:** 6bd65a6
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 1 auto-fixed (1 blocking)
|
||||
**Impact on plan:** Minimal — the pre-existing FLAC writer code was from Plan 02 which would have been next anyway. Integration was straightforward.
|
||||
|
||||
## Issues Encountered
|
||||
None — tests and lint passed on first run after integration.
|
||||
|
||||
## User Setup Required
|
||||
None — no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
- Plan 01 (sqlc queries + MP3 writer) and Plan 02 (FLAC writer) are both complete
|
||||
- Ready for Plan 03 (WriteTrackTags entry point, DB sync pipeline, player safety, scan mutex, events)
|
||||
- All format-specific writers are tested and lint-clean
|
||||
|
||||
---
|
||||
*Phase: 16-tag-writing-database-sync*
|
||||
*Completed: 2026-03-17*
|
||||
Reference in New Issue
Block a user