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
This commit is contained in:
2026-03-20 14:36:18 -04:00
parent 2409b3435a
commit f16157a213
14 changed files with 61 additions and 91 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ linters:
- usestdlibvars
- usetesting
- whitespace
- wsl
- wsl_v5
formatters:
enable:
- gci
+1
View File
@@ -20,6 +20,7 @@ import (
func main() {
source := flag.String("source", "events.go", "path to Go events source file")
output := flag.String("output", "", "path to TypeScript output file (stdout if empty)")
flag.Parse()
consts, err := parseEvents(*source)
+1
View File
@@ -261,6 +261,7 @@ func TestAtomicWrite_SyncAndClose(t *testing.T) {
// Write 1 MiB of data.
const size = 1 << 20 //nolint:mnd
data := bytes.Repeat([]byte("x"), size)
err := AtomicWrite(slog.Default(), target, func(tmp *os.File) error {
-1
View File
@@ -420,7 +420,6 @@ func (l *Library) scanInternal(
return nil
},
)
if walkErr != nil {
metrics.addWarning(
"", "walk",
+3
View File
@@ -54,6 +54,7 @@ func finiteStreamer(n int) beep.Streamer {
func TestBufferedStreamer_BasicStream(t *testing.T) {
const total = 1000
src := finiteStreamer(total)
bs := NewBufferedStreamer(src, 2048)
@@ -120,6 +121,7 @@ func TestBufferedStreamer_BasicStream(t *testing.T) {
func TestBufferedStreamer_SmallReads(t *testing.T) {
const total = 200
src := finiteStreamer(total)
bs := NewBufferedStreamer(src, 512)
@@ -185,6 +187,7 @@ func TestBufferedStreamer_SmallReads(t *testing.T) {
func TestBufferedStreamer_SourceDrained(t *testing.T) {
const total = 100
src := finiteStreamer(total)
bs := NewBufferedStreamer(src, 256)
-1
View File
@@ -802,7 +802,6 @@ func (p *Player) seekLocked(targetSeconds int) error {
return p.seeker.Seek(samples)
}()
if seekErr != nil {
speaker.Unlock()
+2
View File
@@ -514,6 +514,7 @@ func generateSizedVariants(logger *slog.Logger, imgData []byte, dir, hashStr str
src, _, err := image.Decode(bytes.NewReader(imgData))
if err != nil {
logger.Warn("could not decode image for thumbnails", "err", err)
return
}
@@ -537,6 +538,7 @@ func generateSizedVariants(logger *slog.Logger, imgData []byte, dir, hashStr str
var buf bytes.Buffer
if encErr := jpeg.Encode(&buf, dst, &jpeg.Options{Quality: tier.quality}); encErr != nil {
logger.Warn("could not encode thumbnail", "tier", tier.suffix, "err", encErr)
continue
}
+1 -1
View File
@@ -227,10 +227,10 @@ func TestWriteMp3Tags_AtomicSafety(t *testing.T) {
// Attempt to write to a non-existent directory to force AtomicWrite
// to fail (the temp file creation will fail).
badPath := filepath.Join(dir, "nonexistent", "subdir", "file.mp3")
writeErr := writeMp3Tags(testLogger(), badPath, TagChanges{
FieldTitle: "After",
})
if writeErr == nil {
t.Fatal("expected error for non-existent path, got nil")
}
+24 -75
View File
@@ -14,14 +14,14 @@ import (
// Sentinel errors for OGG operations.
var (
errNotOgg = errors.New("not an OGG file")
errNotVorbis = errors.New("not an OGG Vorbis file")
errOggMultiStream = errors.New("this OGG file contains multiple streams and cannot be edited")
errOggTruncated = errors.New("OGG file is truncated")
errOggInvalidPage = errors.New("invalid OGG page")
errOggBadVersion = errors.New("unsupported OGG version")
errOggNoPages = errors.New("OGG file contains no pages")
errOggMissingSetup = errors.New("OGG file is missing the setup header packet")
errNotOgg = errors.New("not an OGG file")
errNotVorbis = errors.New("not an OGG Vorbis file")
errOggMultiStream = errors.New("this OGG file contains multiple streams and cannot be edited")
errOggTruncated = errors.New("OGG file is truncated")
errOggBadVersion = errors.New("unsupported OGG version")
errOggTooFewPackets = errors.New("ogg vorbis: expected at least 3 header packets")
errOggNoPages = errors.New("OGG file contains no pages")
errOggMissingSetup = errors.New("OGG file is missing the setup header packet")
)
// oggCRCTable is the pre-computed 256-entry CRC32 lookup table for OGG.
@@ -117,7 +117,11 @@ func parseOggPages(logger *slog.Logger, filePath string) ([]oggPage, error) {
segEnd := segStart + numSegments
if segEnd > len(raw) {
return nil, fmt.Errorf("%w: segment table truncated at offset %d", errOggTruncated, offset)
return nil, fmt.Errorf(
"%w: segment table truncated at offset %d",
errOggTruncated,
offset,
)
}
segmentTable := make([]byte, numSegments)
@@ -192,7 +196,8 @@ func parseOggPages(logger *slog.Logger, filePath string) ([]oggPage, error) {
// Validate: first page is Vorbis identification header.
firstPacketData := pages[0].data
if len(firstPacketData) < 7 || firstPacketData[0] != 0x01 || //nolint:mnd // Vorbis ID header magic
if len(firstPacketData) < 7 ||
firstPacketData[0] != 0x01 || //nolint:mnd // Vorbis ID header magic
string(firstPacketData[1:7]) != "vorbis" {
return nil, errNotVorbis
}
@@ -249,6 +254,7 @@ func writeOggPage(w io.Writer, page oggPage) error {
// a value <255 terminates it.
func extractPackets(pages []oggPage) [][]byte {
var packets [][]byte
var current []byte
for _, page := range pages {
@@ -299,7 +305,12 @@ func splitPacketIntoSegments(packet []byte) []byte {
// respecting the 255-segment-per-page limit. The first page gets the
// headerType as-is; continuation pages get the continued flag (0x01)
// ORed in. All pages share the same serialNo and granulePos.
func buildPagesFromSegments(segments []byte, packetData []byte, serialNo uint32, granulePos int64) []oggPage {
func buildPagesFromSegments(
segments []byte,
packetData []byte,
serialNo uint32,
granulePos int64,
) []oggPage {
const maxSegmentsPerPage = 255
var pages []oggPage
@@ -400,7 +411,7 @@ func writeOggTags(logger *slog.Logger, filePath string, changes TagChanges) erro
const minHeaderPackets = 3
if len(packets) < minHeaderPackets {
return fmt.Errorf("ogg vorbis: expected at least 3 header packets, got %d", len(packets))
return fmt.Errorf("%w: got %d", errOggTooFewPackets, len(packets))
}
_ = packets[0] // identification packet — preserved as page 0 unchanged
@@ -503,72 +514,10 @@ func findAudioPageStart(pages []oggPage) int {
return len(pages)
}
// serializePacketsToPages is a helper that builds pages from multiple
// packets concatenated into the same page stream. This is used for
// the comment + setup header packets which share pages per Vorbis spec.
func serializePacketsToPages(packets [][]byte, serialNo uint32, granulePos int64) []oggPage {
var allSegs []byte
var allData []byte
for _, pkt := range packets {
segs := splitPacketIntoSegments(pkt)
allSegs = append(allSegs, segs...)
allData = append(allData, pkt...)
}
return buildPagesFromSegments(allSegs, allData, serialNo, granulePos)
}
// computePacketBoundaryInPages determines how many complete packets
// exist starting from the beginning of a sequence of pages, using
// lacing values. It returns the number of complete packets and
// whether the reading ended mid-packet (a continued packet).
func computePacketBoundaryInPages(pages []oggPage) (completedPackets int, midPacket bool) {
inPacket := false
for _, page := range pages {
for _, lacing := range page.segmentTable {
if lacing > 0 {
inPacket = true
}
if lacing < 255 { //nolint:mnd // OGG lacing terminates packet
if inPacket || lacing == 0 {
completedPackets++
inPacket = false
}
}
}
}
return completedPackets, inPacket
}
// oggPagesCopy creates a deep copy of a slice of oggPage.
func oggPagesCopy(pages []oggPage) []oggPage {
cp := make([]oggPage, len(pages))
for i, p := range pages {
cp[i] = oggPage{
headerType: p.headerType,
granulePos: p.granulePos,
serialNo: p.serialNo,
seqNo: p.seqNo,
}
cp[i].segmentTable = make([]byte, len(p.segmentTable))
copy(cp[i].segmentTable, p.segmentTable)
cp[i].data = make([]byte, len(p.data))
copy(cp[i].data, p.data)
}
return cp
}
// pageBytes serializes an OGG page to raw bytes (for CRC verification etc.).
func pageBytes(page oggPage) []byte {
var buf bytes.Buffer
_ = writeOggPage(&buf, page)
return buf.Bytes()
+3 -4
View File
@@ -5,6 +5,7 @@ import (
"encoding/binary"
"os"
"path/filepath"
"strings"
"testing"
"yellowjacket/backend/metadata"
@@ -601,6 +602,7 @@ func TestWriteOggTags_RejectMultiStream(t *testing.T) {
identSegs := splitPacketIntoSegments(identPacket)
const serial1 uint32 = 0x11111111
const serial2 uint32 = 0x22222222
page1 := oggPage{
@@ -652,8 +654,5 @@ func errorContains(err error, substr string) bool {
return false
}
return bytes.Contains(
[]byte(err.Error()),
[]byte(substr),
)
return strings.Contains(err.Error(), substr)
}
+18 -4
View File
@@ -4,11 +4,18 @@ import (
"bytes"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"strconv"
"strings"
)
// Sentinel errors for Vorbis Comment operations.
var (
errVorbisPacketShort = errors.New("vorbis comment packet too short")
errVorbisInvalidMagic = errors.New("invalid vorbis comment header magic")
)
// oggVorbisComment holds a parsed Vorbis Comment structure with raw
// byte preservation for non-edited fields (even if they contain
// invalid UTF-8).
@@ -24,11 +31,12 @@ func parseVorbisCommentPacket(packet []byte) (*oggVorbisComment, error) {
const prefixLen = 7 // \x03 + "vorbis"
if len(packet) < prefixLen {
return nil, fmt.Errorf("vorbis comment packet too short: %d bytes", len(packet))
return nil, fmt.Errorf("%w: %d bytes", errVorbisPacketShort, len(packet))
}
if packet[0] != 0x03 || string(packet[1:7]) != "vorbis" { //nolint:mnd // Vorbis comment header magic
return nil, fmt.Errorf("invalid vorbis comment header magic")
if packet[0] != 0x03 ||
string(packet[1:7]) != "vorbis" { //nolint:mnd // Vorbis comment header magic
return nil, errVorbisInvalidMagic
}
r := bytes.NewReader(packet[prefixLen:])
@@ -200,7 +208,13 @@ func buildMetadataBlockPicture(imageData []byte) []byte {
// Calculate size: type(4) + mimeLen(4) + mime + descLen(4) + desc
// + width(4) + height(4) + depth(4) + colors(4) + dataLen(4) + data
size := 4 + 4 + len(mime) + 4 + len(desc) + 4*4 + 4 + len(imageData) //nolint:mnd // FLAC PICTURE block fields
size := 4 + 4 + len(
mime,
) + 4 + len(
desc,
) + 4*4 + 4 + len(
imageData,
) //nolint:mnd // FLAC PICTURE block fields
buf := make([]byte, 0, size)
+4 -2
View File
@@ -244,11 +244,13 @@ func (tw *TagWriter) BatchWriteTrackTags(
// Set up cancellation channel.
tw.cancelBatch = make(chan struct{})
defer func() { tw.cancelBatch = nil }()
// Suppress per-track TrackMetadataChanged events — we emit one
// at the end instead.
tw.suppressEvents = true
defer func() { tw.suppressEvents = false }()
for i, filePath := range filePaths {
@@ -256,12 +258,11 @@ func (tw *TagWriter) BatchWriteTrackTags(
select {
case <-tw.cancelBatch:
result.Cancelled = true
tw.logger.Info("batch write cancelled",
"at", i,
"total", total,
)
break
default:
}
@@ -276,6 +277,7 @@ func (tw *TagWriter) BatchWriteTrackTags(
FilePath: filePath,
Error: err.Error(),
})
tw.logger.Warn("batch track failed",
"path", filePath,
"err", err,
+1
View File
@@ -99,6 +99,7 @@ func asBytes(v any) ([]byte, bool) {
}
out := make([]byte, len(arr))
for i, elem := range arr {
f, ok := elem.(float64)
if !ok {
+2 -2
View File
@@ -32,6 +32,7 @@ func createTestWAV(
// --- data chunk: 200 bytes of silence ---
const silenceLen = 200 //nolint:mnd
silence := make([]byte, silenceLen)
// --- optional id3 chunk ---
@@ -401,7 +402,6 @@ func TestWriteWavTags_AtomicSafety(t *testing.T) {
writeErr := writeWavTags(testLogger(), badPath, TagChanges{
FieldTitle: "After",
})
if writeErr == nil {
t.Fatal("expected error for non-existent path, got nil")
}
@@ -443,7 +443,6 @@ func TestWriteWavTags_RejectsRF64(t *testing.T) {
err := writeWavTags(testLogger(), path, TagChanges{
FieldTitle: "Should Fail",
})
if err == nil {
t.Fatal("expected error for RF64 file, got nil")
}
@@ -601,6 +600,7 @@ func createTestWAVWithExtraChunks(
// 200 bytes of silence for data chunk.
const silenceLen = 200 //nolint:mnd
silence := make([]byte, silenceLen)
// Calculate RIFF payload.