H-15: the default columns were track, artist and duration, so a library manager with duplicate detection could not tell its own duplicate fixtures apart by eye. Album is a default now, in Go and in the frontend fallback — both, because a fresh install persists the Go list and the UI renders the TS one until the config arrives. It does not deliver the finding's stated benefit, and that is worth recording: the three `Tideline / Aurora Fields / 00:06` rows are duplicates of the same album, so they read identically with an Album column too. What tells them apart is the duplicate-detection feature or a file path column, not this. Album is still the right default for every other row in the list. smart-playlist-details joins search-store's scope map. Checked before adding, as asked: it reads searchCtrl.term in getVisibleTracks and prints the term in the page, so the header box was disabled and unlabelled on a view that filters as you type — the fix is a scope entry, not a disabled state with a reason.
124 lines
3.0 KiB
Go
124 lines
3.0 KiB
Go
// Package tracklist manages track-list display configuration.
|
|
package tracklist
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
)
|
|
|
|
var (
|
|
errUnknownColumnID = errors.New("unknown track-list column ID")
|
|
errDuplicateColumn = errors.New("duplicate column ID")
|
|
)
|
|
|
|
// ColumnID identifies a displayable column in the track list.
|
|
type ColumnID string
|
|
|
|
// Valid column identifiers.
|
|
const (
|
|
ColTrackName ColumnID = "trackName"
|
|
ColArtistName ColumnID = "artistName"
|
|
ColTrackLength ColumnID = "trackLength"
|
|
ColAlbum ColumnID = "album"
|
|
ColGenre ColumnID = "genre"
|
|
ColYear ColumnID = "year"
|
|
ColComposer ColumnID = "composer"
|
|
ColTrackNumber ColumnID = "trackNumber"
|
|
ColDiscNumber ColumnID = "discNumber"
|
|
ColFilePath ColumnID = "filePath"
|
|
ColFileType ColumnID = "fileType"
|
|
ColSampleRate ColumnID = "sampleRate"
|
|
ColBitDepth ColumnID = "bitDepth"
|
|
ColChannels ColumnID = "channels"
|
|
ColBitrate ColumnID = "bitrate"
|
|
ColFileSize ColumnID = "fileSize"
|
|
ColPlayCount ColumnID = "playCount"
|
|
ColAlbumArt ColumnID = "albumArt"
|
|
)
|
|
|
|
// AllColumnIDs lists every recognised column in default display
|
|
// order.
|
|
var AllColumnIDs = []ColumnID{
|
|
ColAlbumArt,
|
|
ColTrackName,
|
|
ColArtistName,
|
|
ColTrackLength,
|
|
ColAlbum,
|
|
ColGenre,
|
|
ColYear,
|
|
ColComposer,
|
|
ColTrackNumber,
|
|
ColDiscNumber,
|
|
ColFilePath,
|
|
ColFileType,
|
|
ColSampleRate,
|
|
ColBitDepth,
|
|
ColChannels,
|
|
ColBitrate,
|
|
ColFileSize,
|
|
ColPlayCount,
|
|
}
|
|
|
|
// DefaultColumns is the initial column configuration. Album is in it
|
|
// (H-15): without it the three identical `Tideline / Aurora Fields /
|
|
// 00:06` rows in this app's own fixture library cannot be told apart,
|
|
// in an app that has a duplicate-detection feature. Must match
|
|
// DEFAULT_COLUMN_IDS in frontend/src/components/track-list/columns.ts,
|
|
// which is what the UI falls back to before the config arrives.
|
|
var DefaultColumns = []Column{
|
|
{ID: ColTrackName},
|
|
{ID: ColArtistName},
|
|
{ID: ColAlbum},
|
|
{ID: ColTrackLength},
|
|
}
|
|
|
|
// Column represents a visible column in the track list.
|
|
type Column struct {
|
|
ID ColumnID `json:"id" toml:"ID"`
|
|
}
|
|
|
|
// Config holds track-list display preferences.
|
|
type Config struct {
|
|
Columns []Column `json:"columns" toml:"Columns"`
|
|
}
|
|
|
|
// ApplyDefaults fills zero-value fields with sensible defaults.
|
|
func (c *Config) ApplyDefaults() {
|
|
if len(c.Columns) == 0 {
|
|
c.Columns = make([]Column, len(DefaultColumns))
|
|
copy(c.Columns, DefaultColumns)
|
|
}
|
|
}
|
|
|
|
// Validate checks that every column ID is recognised and that
|
|
// there are no duplicates.
|
|
func (c *Config) Validate() error {
|
|
c.ApplyDefaults()
|
|
|
|
seen := make(map[ColumnID]bool, len(c.Columns))
|
|
|
|
for _, col := range c.Columns {
|
|
if !isValidColumnID(col.ID) {
|
|
return fmt.Errorf(
|
|
"%w: %q", errUnknownColumnID, col.ID,
|
|
)
|
|
}
|
|
|
|
if seen[col.ID] {
|
|
return fmt.Errorf(
|
|
"%w: %q", errDuplicateColumn, col.ID,
|
|
)
|
|
}
|
|
|
|
seen[col.ID] = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// isValidColumnID returns true when id matches a known column.
|
|
func isValidColumnID(id ColumnID) bool {
|
|
return slices.Contains(AllColumnIDs, id)
|
|
}
|