release / govulncheck (push) Successful in 20s
release / web (push) Successful in 1m20s
release / go (push) Successful in 1m37s
release / integration (push) Successful in 5m15s
release / android (push) Successful in 5m26s
release / Build signed APK (releases and dev) (push) Successful in 5m45s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 16s
release / Verify release artifacts (tag releases only) (push) Skipped
Rewrites the internal/library comment blocks to rule 32. Task, milestone, decision and rule ids are gone; the incidents behind them are restated as the constraint they left (why a file is deleted before its row, why the missing mark is a mark, why duplicates block on duration). Counts and album names from one library are dropped from production comments; test comments keep the names their fixtures use. The tagReadVersion log keeps each version's before and after, without the measurements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
3.1 KiB
Go
84 lines
3.1 KiB
Go
package library
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
|
)
|
|
|
|
// NormalizationPrefs is one user's loudness-normalization preference. It lives
|
|
// on the server so every player applies the same one: the web player and the
|
|
// Android app read it, and the leveled stream for UPnP/Sonos renders with it.
|
|
type NormalizationPrefs struct {
|
|
// Mode is off, auto, track or album. Auto uses album gain while an album
|
|
// plays in order and track gain otherwise.
|
|
Mode string `json:"mode"`
|
|
// TargetLUFS is the loudness tracks are leveled to.
|
|
TargetLUFS int16 `json:"target_lufs"`
|
|
// Boost is how a track quieter than the target is raised: headroom stops
|
|
// at its true peak, limiter goes all the way and limits the peaks.
|
|
Boost string `json:"boost"`
|
|
}
|
|
|
|
// The accepted values. Migration 0067's CHECKs hold the same lists.
|
|
var (
|
|
NormalizationModes = []string{"off", "auto", "track", "album"}
|
|
NormalizationTargets = []int16{-18, -16, -14}
|
|
NormalizationBoosts = []string{"headroom", "limiter"}
|
|
)
|
|
|
|
// DefaultNormalizationPrefs applies to a user who has never chosen. It must
|
|
// equal migration 0067's column defaults; a test holds them together.
|
|
var DefaultNormalizationPrefs = NormalizationPrefs{Mode: "auto", TargetLUFS: -18, Boost: "headroom"}
|
|
|
|
// ErrNormalizationPrefsInvalid is returned for a value outside the lists.
|
|
var ErrNormalizationPrefsInvalid = errors.New("normalization preference out of range")
|
|
|
|
// Validate reports the first field holding a value the server does not
|
|
// accept.
|
|
func (p NormalizationPrefs) Validate() error {
|
|
switch {
|
|
case !slices.Contains(NormalizationModes, p.Mode):
|
|
return fmt.Errorf("%w: mode %q", ErrNormalizationPrefsInvalid, p.Mode)
|
|
case !slices.Contains(NormalizationTargets, p.TargetLUFS):
|
|
return fmt.Errorf("%w: target_lufs %d", ErrNormalizationPrefsInvalid, p.TargetLUFS)
|
|
case !slices.Contains(NormalizationBoosts, p.Boost):
|
|
return fmt.Errorf("%w: boost %q", ErrNormalizationPrefsInvalid, p.Boost)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LoadNormalizationPrefs returns the user's preference, or the defaults if
|
|
// they have never set one.
|
|
func LoadNormalizationPrefs(ctx context.Context, q *dbq.Queries, userID pgtype.UUID) (NormalizationPrefs, error) {
|
|
row, err := q.GetNormalizationPrefs(ctx, userID)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return DefaultNormalizationPrefs, nil
|
|
}
|
|
if err != nil {
|
|
return DefaultNormalizationPrefs, err
|
|
}
|
|
return NormalizationPrefs{Mode: row.Mode, TargetLUFS: row.TargetLufs, Boost: row.Boost}, nil
|
|
}
|
|
|
|
// SaveNormalizationPrefs validates and stores the user's preference,
|
|
// returning what was stored.
|
|
func SaveNormalizationPrefs(ctx context.Context, q *dbq.Queries, userID pgtype.UUID, p NormalizationPrefs) (NormalizationPrefs, error) {
|
|
if err := p.Validate(); err != nil {
|
|
return p, err
|
|
}
|
|
row, err := q.UpsertNormalizationPrefs(ctx, dbq.UpsertNormalizationPrefsParams{
|
|
UserID: userID, Mode: p.Mode, TargetLufs: p.TargetLUFS, Boost: p.Boost,
|
|
})
|
|
if err != nil {
|
|
return p, err
|
|
}
|
|
return NormalizationPrefs{Mode: row.Mode, TargetLUFS: row.TargetLufs, Boost: row.Boost}, nil
|
|
}
|