refactor: multiple syntax updates for Go 1.26

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan
2026-05-19 18:02:29 -03:00
parent 545a9ecc3c
commit efe9291db0
51 changed files with 143 additions and 251 deletions
+2 -1
View File
@@ -37,5 +37,6 @@ AGENTS.md
*.wasm
*.ndp
openspec/
.agents
go.work*
.worktrees/
.worktrees/
+4 -6
View File
@@ -8,7 +8,6 @@ import (
"github.com/djherbis/times"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/metadata"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -91,8 +90,7 @@ var _ = Describe("Extractor", func() {
info.FileInfo = testFileInfo{FileInfo: fileInfo}
metadata := metadata.New(path, info)
mf := metadata.ToMediaFile(1, "folderID")
return &mf
return new(metadata.ToMediaFile(1, "folderID"))
}
BeforeEach(func() {
@@ -109,7 +107,7 @@ var _ = Describe("Extractor", func() {
Expect(mf.RGAlbumPeak).To(Equal(albumPeak))
},
Entry("mp3 with no replaygain", "no_replaygain.mp3", nil, nil, nil, nil),
Entry("mp3 with no zero replaygain", "zero_replaygain.mp3", gg.P(0.0), gg.P(1.0), gg.P(0.0), gg.P(1.0)),
Entry("mp3 with no zero replaygain", "zero_replaygain.mp3", new(0.0), new(1.0), new(0.0), new(1.0)),
)
})
@@ -120,8 +118,8 @@ var _ = Describe("Extractor", func() {
DisplayTitle: "",
Lang: code,
Line: []model.Line{
{Start: gg.P(int64(0)), Value: "This is"},
{Start: gg.P(int64(2500)), Value: secondLine},
{Start: new(int64(0)), Value: "This is"},
{Start: new(int64(2500)), Value: secondLine},
},
Offset: nil,
Synced: true,
+2 -2
View File
@@ -153,7 +153,7 @@ func (e *provider) populateAlbumInfo(ctx context.Context, album auxAlbum) (auxAl
return album, err
}
album.ExternalInfoUpdatedAt = P(time.Now())
album.ExternalInfoUpdatedAt = new(time.Now())
album.ExternalUrl = info.URL
if info.Description != "" {
@@ -269,7 +269,7 @@ func (e *provider) populateArtistInfo(ctx context.Context, artist auxArtist) (au
return artist, ctx.Err()
}
artist.ExternalInfoUpdatedAt = P(time.Now())
artist.ExternalInfoUpdatedAt = new(time.Now())
err := e.ds.Artist(ctx).UpdateExternalInfo(&artist.Artist)
if err != nil {
log.Error(ctx, "Error trying to update artist external information", "id", artist.ID, "name", artistName,
+2 -4
View File
@@ -272,12 +272,11 @@ var _ = Describe("Provider - ArtistImage", func() {
It("returns cached URL and does not call agent when info is not expired", func() {
// Arrange: artist has a cached image URL with recent ExternalInfoUpdatedAt
recentTime := time.Now().Add(-1 * time.Minute)
cachedArtist := &model.Artist{
ID: "artist-cached",
Name: "Cached Artist",
LargeImageUrl: "http://example.com/cached-large.jpg",
ExternalInfoUpdatedAt: &recentTime,
ExternalInfoUpdatedAt: new(time.Now().Add(-1 * time.Minute)),
}
mockArtistRepo.On("Get", "artist-cached").Return(cachedArtist, nil).Maybe()
expectedURL, _ := url.Parse("http://example.com/cached-large.jpg")
@@ -304,12 +303,11 @@ var _ = Describe("Provider - ArtistImage", func() {
It("returns stale URL and enqueues refresh when info is expired", func() {
// Arrange
conf.Server.DevArtistInfoTimeToLive = 1 * time.Nanosecond
expiredTime := time.Now().Add(-1 * time.Hour)
staleArtist := &model.Artist{
ID: "artist-expired",
Name: "Expired Artist",
LargeImageUrl: "http://example.com/expired-large.jpg",
ExternalInfoUpdatedAt: &expiredTime,
ExternalInfoUpdatedAt: new(time.Now().Add(-1 * time.Hour)),
}
mockArtistRepo.On("Get", "artist-expired").Return(staleArtist, nil).Maybe()
expectedURL, _ := url.Parse("http://example.com/expired-large.jpg")
+2 -3
View File
@@ -12,7 +12,6 @@ import (
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/mock"
@@ -90,7 +89,7 @@ var _ = Describe("Provider - UpdateAlbumInfo", func() {
ExternalUrl: "http://cached.com/album",
Description: "Cached Desc",
LargeImageUrl: "http://cached.com/large.jpg",
ExternalInfoUpdatedAt: gg.P(now.Add(-conf.Server.DevAlbumInfoTimeToLive / 2)),
ExternalInfoUpdatedAt: new(now.Add(-conf.Server.DevAlbumInfoTimeToLive / 2)),
}
mockAlbumRepo.SetData(model.Albums{*originalAlbum})
@@ -113,7 +112,7 @@ var _ = Describe("Provider - UpdateAlbumInfo", func() {
ExternalUrl: "http://expired.com/album",
Description: "Expired Desc",
LargeImageUrl: "http://expired.com/large.jpg",
ExternalInfoUpdatedAt: gg.P(expiredTime),
ExternalInfoUpdatedAt: new(expiredTime),
}
mockAlbumRepo.SetData(model.Albums{*originalAlbum})
+3 -4
View File
@@ -13,7 +13,6 @@ import (
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/mock"
@@ -137,7 +136,7 @@ var _ = Describe("Provider - UpdateArtistInfo", func() {
ExternalUrl: "http://cached.url",
Biography: "Cached Bio",
LargeImageUrl: "http://cached_large.jpg",
ExternalInfoUpdatedAt: gg.P(now.Add(-conf.Server.DevArtistInfoTimeToLive / 2)),
ExternalInfoUpdatedAt: new(now.Add(-conf.Server.DevArtistInfoTimeToLive / 2)),
SimilarArtists: model.Artists{
{ID: "ar-similar-present", Name: "Similar Present"},
{ID: "ar-similar-absent", Name: "Similar Absent"},
@@ -174,7 +173,7 @@ var _ = Describe("Provider - UpdateArtistInfo", func() {
originalArtist := &model.Artist{
ID: "ar-expired",
Name: "Expired Artist",
ExternalInfoUpdatedAt: gg.P(expiredTime),
ExternalInfoUpdatedAt: new(expiredTime),
SimilarArtists: model.Artists{
{ID: "ar-exp-similar", Name: "Expired Similar"},
},
@@ -205,7 +204,7 @@ var _ = Describe("Provider - UpdateArtistInfo", func() {
originalArtist := &model.Artist{
ID: "ar-similar-test",
Name: "Similar Test Artist",
ExternalInfoUpdatedAt: gg.P(now.Add(-conf.Server.DevArtistInfoTimeToLive / 2)),
ExternalInfoUpdatedAt: new(now.Add(-conf.Server.DevArtistInfoTimeToLive / 2)),
SimilarArtists: model.Artists{
{ID: "ar-sim-present", Name: "Similar Present"},
{ID: "", Name: "Similar Absent Raw"},
+1 -2
View File
@@ -326,8 +326,7 @@ func (j *ffCmd) start(ctx context.Context) error {
func (j *ffCmd) wait() {
if err := j.cmd.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok {
errMsg := fmt.Sprintf("%s exited with non-zero status code: %d", j.args[0], exitErr.ExitCode())
if stderrOutput := strings.TrimSpace(j.stderr.String()); stderrOutput != "" {
errMsg += ": " + stderrOutput
+1 -2
View File
@@ -7,7 +7,6 @@ import (
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/metadata"
. "github.com/navidrome/navidrome/utils/gg"
)
type InspectOutput struct {
@@ -44,7 +43,7 @@ func Inspect(filePath string, libraryId int, folderId string) (*InspectOutput, e
result := &InspectOutput{
File: filePath,
RawTags: tags[file].Tags,
MappedTags: P(md.ToMediaFile(libraryId, folderId)),
MappedTags: new(md.ToMediaFile(libraryId, folderId)),
}
return result, nil
+3 -4
View File
@@ -12,7 +12,6 @@ import (
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
"github.com/navidrome/navidrome/utils"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -32,15 +31,15 @@ var _ = Describe("sources", func() {
Lang: "eng",
Line: []model.Line{
{
Start: gg.P(int64(18800)),
Start: new(int64(18800)),
Value: "We're no strangers to love",
},
{
Start: gg.P(int64(22801)),
Start: new(int64(22801)),
Value: "You know the rules and so do I",
},
},
Offset: gg.P(int64(-100)),
Offset: new(int64(-100)),
Synced: true,
},
}
+6 -7
View File
@@ -5,7 +5,6 @@ import (
"encoding/json"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -74,15 +73,15 @@ var _ = Describe("sources", func() {
Lang: "eng",
Line: []model.Line{
{
Start: gg.P(int64(18800)),
Start: new(int64(18800)),
Value: "We're no strangers to love",
},
{
Start: gg.P(int64(22801)),
Start: new(int64(22801)),
Value: "You know the rules and so do I",
},
},
Offset: gg.P(int64(-100)),
Offset: new(int64(-100)),
Synced: true,
},
}))
@@ -122,7 +121,7 @@ var _ = Describe("sources", func() {
// The critical assertion: even with BOM, synced should be true
Expect(lyrics[0].Synced).To(BeTrue(), "Lyrics with BOM marker should be recognized as synced")
Expect(lyrics[0].Line).To(HaveLen(1))
Expect(lyrics[0].Line[0].Start).To(Equal(gg.P(int64(0))))
Expect(lyrics[0].Line[0].Start).To(Equal(new(int64(0))))
Expect(lyrics[0].Line[0].Value).To(ContainSubstring("作曲"))
})
@@ -137,9 +136,9 @@ var _ = Describe("sources", func() {
// UTF-16 should be properly converted to UTF-8
Expect(lyrics[0].Synced).To(BeTrue(), "UTF-16 encoded lyrics should be recognized as synced")
Expect(lyrics[0].Line).To(HaveLen(2))
Expect(lyrics[0].Line[0].Start).To(Equal(gg.P(int64(18800))))
Expect(lyrics[0].Line[0].Start).To(Equal(new(int64(18800))))
Expect(lyrics[0].Line[0].Value).To(Equal("We're no strangers to love"))
Expect(lyrics[0].Line[1].Start).To(Equal(gg.P(int64(22801))))
Expect(lyrics[0].Line[1].Start).To(Equal(new(int64(22801))))
Expect(lyrics[0].Line[1].Value).To(Equal("You know the rules and so do I"))
})
})
+1 -2
View File
@@ -62,8 +62,7 @@ func (j *Executor) start(ctx context.Context) error {
func (j *Executor) wait() {
if err := j.cmd.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok {
_ = j.out.CloseWithError(fmt.Errorf("%s exited with non-zero status code: %d", j.args[0], exitErr.ExitCode()))
} else {
_ = j.out.CloseWithError(fmt.Errorf("waiting %s cmd: %w", j.args[0], err))
+1 -2
View File
@@ -59,8 +59,7 @@ func (s *playlists) parseNSP(_ context.Context, pls *model.Playlist, reader io.R
}
err = json.Unmarshal(input, nsp)
if err != nil {
var syntaxErr *json.SyntaxError
if errors.As(err, &syntaxErr) {
if syntaxErr, ok := errors.AsType[*json.SyntaxError](err); ok {
line, col := getPositionFromOffset(input, syntaxErr.Offset)
return fmt.Errorf("JSON syntax error in SmartPlaylist at line %d, column %d: %w", line, col, err)
}
+5 -10
View File
@@ -144,29 +144,25 @@ var _ = Describe("Playlists", func() {
It("allows owner to update their playlist", func() {
ctx = request.WithUser(ctx, model.User{ID: "user-1", IsAdmin: false})
newName := "Updated Name"
err := ps.Update(ctx, "pls-1", &newName, nil, nil, nil, nil)
err := ps.Update(ctx, "pls-1", new("Updated Name"), nil, nil, nil, nil)
Expect(err).ToNot(HaveOccurred())
})
It("allows admin to update any playlist", func() {
ctx = request.WithUser(ctx, model.User{ID: "admin-1", IsAdmin: true})
newName := "Updated Name"
err := ps.Update(ctx, "pls-other", &newName, nil, nil, nil, nil)
err := ps.Update(ctx, "pls-other", new("Updated Name"), nil, nil, nil, nil)
Expect(err).ToNot(HaveOccurred())
})
It("denies non-owner, non-admin from updating", func() {
ctx = request.WithUser(ctx, model.User{ID: "other-user", IsAdmin: false})
newName := "Updated Name"
err := ps.Update(ctx, "pls-1", &newName, nil, nil, nil, nil)
err := ps.Update(ctx, "pls-1", new("Updated Name"), nil, nil, nil, nil)
Expect(err).To(MatchError(model.ErrNotAuthorized))
})
It("returns error when playlist not found", func() {
ctx = request.WithUser(ctx, model.User{ID: "user-1", IsAdmin: false})
newName := "Updated Name"
err := ps.Update(ctx, "nonexistent", &newName, nil, nil, nil, nil)
err := ps.Update(ctx, "nonexistent", new("Updated Name"), nil, nil, nil, nil)
Expect(err).To(Equal(model.ErrNotFound))
})
@@ -184,8 +180,7 @@ var _ = Describe("Playlists", func() {
It("allows metadata updates on a smart playlist", func() {
ctx = request.WithUser(ctx, model.User{ID: "user-1", IsAdmin: false})
newName := "Updated Smart"
err := ps.Update(ctx, "pls-smart", &newName, nil, nil, nil, nil)
err := ps.Update(ctx, "pls-smart", new("Updated Smart"), nil, nil, nil, nil)
Expect(err).ToNot(HaveOccurred())
})
})
+1 -2
View File
@@ -63,7 +63,6 @@ var _ = Describe("REST Adapter", func() {
It("clears server-managed fields to prevent injection via REST API", func() {
ctx = request.WithUser(ctx, model.User{ID: "user-1", IsAdmin: false})
repo = ps.NewRepository(ctx).(rest.Persistable)
now := time.Now()
pls := &model.Playlist{
Name: "Legit Playlist",
Comment: "A comment",
@@ -73,7 +72,7 @@ var _ = Describe("REST Adapter", func() {
Sync: true,
UploadedImage: "injected-image-path",
ExternalImageURL: "http://evil.example.com/ssrf",
EvaluatedAt: &now,
EvaluatedAt: new(time.Now()),
}
_, err := repo.Save(pls)
Expect(err).ToNot(HaveOccurred())
+1 -2
View File
@@ -1133,8 +1133,7 @@ func (f *fakeScrobbler) PlaybackReport(ctx context.Context, info PlaybackSession
if f.Error != nil {
return f.Error
}
uid := info.UserId
f.userID.Store(&uid)
f.userID.Store(new(info.UserId))
f.LastPlaybackReport.Store(&info)
return nil
}
+2 -2
View File
@@ -41,7 +41,7 @@ func (s *shareService) Load(ctx context.Context, id string) (*model.Share, error
if !expiresAt.IsZero() && expiresAt.Before(time.Now()) {
return nil, model.ErrExpired
}
share.LastVisitedAt = P(time.Now())
share.LastVisitedAt = new(time.Now())
share.VisitCount++
err = repo.(rest.Persistable).Update(id, share, "last_visited_at", "visit_count")
@@ -95,7 +95,7 @@ func (r *shareRepositoryWrapper) Save(entity any) (string, error) {
}
s.ID = id
if V(s.ExpiresAt).IsZero() {
s.ExpiresAt = P(time.Now().Add(conf.Server.DefaultShareExpiration))
s.ExpiresAt = new(time.Now().Add(conf.Server.DefaultShareExpiration))
}
firstId := strings.SplitN(s.ResourceIDs, ",", 2)[0]
+1 -2
View File
@@ -11,8 +11,7 @@ import (
var _ = Describe("ArtworkID", func() {
Describe("NewArtworkID()", func() {
It("creates a valid parseable ArtworkID", func() {
now := time.Now()
id := model.NewArtworkID(model.KindAlbumArtwork, "1234", &now)
id := model.NewArtworkID(model.KindAlbumArtwork, "1234", new(time.Now()))
parsedId, err := model.ParseArtworkID(id.String())
Expect(err).ToNot(HaveOccurred())
Expect(parsedId.Kind).To(Equal(id.Kind))
+22 -30
View File
@@ -8,14 +8,13 @@ import (
var _ = Describe("ToLyrics", func() {
It("should parse tags with spaces", func() {
num := int64(1551)
lyrics, err := ToLyrics("xxx", "[lang: eng ]\n[offset: 1551 ]\n[ti: A title ]\n[ar: An artist ]\n[00:00.00]Hi there")
Expect(err).ToNot(HaveOccurred())
Expect(lyrics.Lang).To(Equal("eng"))
Expect(lyrics.Synced).To(BeTrue())
Expect(lyrics.DisplayArtist).To(Equal("An artist"))
Expect(lyrics.DisplayTitle).To(Equal("A title"))
Expect(lyrics.Offset).To(Equal(&num))
Expect(lyrics.Offset).To(Equal(new(int64(1551))))
})
It("Should ignore bad offset", func() {
@@ -25,39 +24,36 @@ var _ = Describe("ToLyrics", func() {
})
It("should accept lines with no text and weird times", func() {
a, b, c, d := int64(0), int64(10040), int64(40000), int64(1000*60*60)
lyrics, err := ToLyrics("xxx", "[00:00.00]Hi there\n\n\n[00:10.040]\n[00:40]Test\n[01:00:00]late")
Expect(err).ToNot(HaveOccurred())
Expect(lyrics.Synced).To(BeTrue())
Expect(lyrics.Line).To(Equal([]Line{
{Start: &a, Value: "Hi there"},
{Start: &b, Value: ""},
{Start: &c, Value: "Test"},
{Start: &d, Value: "late"},
{Start: new(int64(0)), Value: "Hi there"},
{Start: new(int64(10040)), Value: ""},
{Start: new(int64(40000)), Value: "Test"},
{Start: new(int64(1000 * 60 * 60)), Value: "late"},
}))
})
It("Should support multiple timestamps per line", func() {
a, b, c, d := int64(0), int64(10000), int64(13*60*1000), int64(1000*60*60*51)
lyrics, err := ToLyrics("xxx", "[00:00.00] [00:10.00]Repeated\n[13:00][51:00:00.00]")
Expect(err).ToNot(HaveOccurred())
Expect(lyrics.Synced).To(BeTrue())
Expect(lyrics.Line).To(Equal([]Line{
{Start: &a, Value: "Repeated"},
{Start: &b, Value: "Repeated"},
{Start: &c, Value: ""},
{Start: &d, Value: ""},
{Start: new(int64(0)), Value: "Repeated"},
{Start: new(int64(10000)), Value: "Repeated"},
{Start: new(int64(13 * 60 * 1000)), Value: ""},
{Start: new(int64(1000 * 60 * 60 * 51)), Value: ""},
}))
})
It("Should support parsing multiline string", func() {
a, b := int64(0), int64(10*60*1000+1)
lyrics, err := ToLyrics("xxx", "[00:00.00]This is\na multiline \n\n [:0] string\n[10:00.001]This is\nalso one")
Expect(err).ToNot(HaveOccurred())
Expect(lyrics.Synced).To(BeTrue())
Expect(lyrics.Line).To(Equal([]Line{
{Start: &a, Value: "This is\na multiline\n\n[:0] string"},
{Start: &b, Value: "This is\nalso one"},
{Start: new(int64(0)), Value: "This is\na multiline\n\n[:0] string"},
{Start: new(int64(10*60*1000 + 1)), Value: "This is\nalso one"},
}))
})
@@ -71,49 +67,45 @@ var _ = Describe("ToLyrics", func() {
})
It("Allows timestamp in middle of line if also at beginning", func() {
a, b := int64(0), int64(1000)
lyrics, err := ToLyrics("xxx", " [00:00] This is [00:00:00] be a synced file\n [00:01]Line 2")
Expect(err).ToNot(HaveOccurred())
Expect(lyrics.Synced).To(BeTrue())
Expect(lyrics.Line).To(Equal([]Line{
{Start: &a, Value: "This is [00:00:00] be a synced file"},
{Start: &b, Value: "Line 2"},
{Start: new(int64(0)), Value: "This is [00:00:00] be a synced file"},
{Start: new(int64(1000)), Value: "Line 2"},
}))
})
It("Ignores lines in synchronized lyric prior to first timestamp", func() {
a := int64(0)
lyrics, err := ToLyrics("xxx", "This is some prelude\nThat doesn't\nmatter\n[00:00]Text")
Expect(err).ToNot(HaveOccurred())
Expect(lyrics.Synced).To(BeTrue())
Expect(lyrics.Line).To(Equal([]Line{
{Start: &a, Value: "Text"},
{Start: new(int64(0)), Value: "Text"},
}))
})
It("Handles all possible ms cases", func() {
a, b, c := int64(1), int64(10), int64(100)
lyrics, err := ToLyrics("xxx", "[00:00.001]a\n[00:00.01]b\n[00:00.1]c")
Expect(err).ToNot(HaveOccurred())
Expect(lyrics.Synced).To(BeTrue())
Expect(lyrics.Line).To(Equal([]Line{
{Start: &a, Value: "a"},
{Start: &b, Value: "b"},
{Start: &c, Value: "c"},
{Start: new(int64(1)), Value: "a"},
{Start: new(int64(10)), Value: "b"},
{Start: new(int64(100)), Value: "c"},
}))
})
It("Properly sorts repeated lyrics out of order", func() {
a, b, c, d, e := int64(0), int64(10000), int64(40000), int64(13*60*1000), int64(1000*60*60*51)
lyrics, err := ToLyrics("xxx", "[00:00.00] [13:00]Repeated\n[00:10.00][51:00:00.00]Test\n[00:40.00]Not repeated")
Expect(err).ToNot(HaveOccurred())
Expect(lyrics.Synced).To(BeTrue())
Expect(lyrics.Line).To(Equal([]Line{
{Start: &a, Value: "Repeated"},
{Start: &b, Value: "Test"},
{Start: &c, Value: "Not repeated"},
{Start: &d, Value: "Repeated"},
{Start: &e, Value: "Test"},
{Start: new(int64(0)), Value: "Repeated"},
{Start: new(int64(10000)), Value: "Test"},
{Start: new(int64(40000)), Value: "Not repeated"},
{Start: new(int64(13 * 60 * 1000)), Value: "Repeated"},
{Start: new(int64(1000 * 60 * 60 * 51)), Value: "Test"},
}))
})
})
+2 -3
View File
@@ -8,7 +8,6 @@ import (
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/metadata"
"github.com/navidrome/navidrome/tests"
. "github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -108,8 +107,8 @@ var _ = Describe("ToMediaFile", func() {
expected := model.LyricList{
{Lang: "eng", Line: []model.Line{
{Value: "This is", Start: P(int64(0))},
{Value: "English SYLT", Start: P(int64(2500))},
{Value: "This is", Start: new(int64(0))},
{Value: "English SYLT", Start: new(int64(2500))},
}, Synced: true},
{Lang: "xxx", Line: []model.Line{{Value: "Lyrics"}}, Synced: false},
}
+7 -8
View File
@@ -8,7 +8,6 @@ import (
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/metadata"
"github.com/navidrome/navidrome/utils"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -274,8 +273,8 @@ var _ = Describe("Metadata", func() {
mf := createMF("replaygain_track_gain", tagValue)
Expect(mf.RGTrackGain).To(Equal(expected))
},
Entry("0", "0", gg.P(0.0)),
Entry("1.2dB", "1.2dB", gg.P(1.2)),
Entry("0", "0", new(0.0)),
Entry("1.2dB", "1.2dB", new(1.2)),
Entry("Infinity", "Infinity", nil),
Entry("Invalid value", "INVALID VALUE", nil),
Entry("NaN", "NaN", nil),
@@ -285,9 +284,9 @@ var _ = Describe("Metadata", func() {
mf := createMF("replaygain_track_peak", tagValue)
Expect(mf.RGTrackPeak).To(Equal(expected))
},
Entry("0", "0", gg.P(0.0)),
Entry("1.0", "1.0", gg.P(1.0)),
Entry("0.5", "0.5", gg.P(0.5)),
Entry("0", "0", new(0.0)),
Entry("1.0", "1.0", new(1.0)),
Entry("0.5", "0.5", new(0.5)),
Entry("Invalid dB suffix", "0.7dB", nil),
Entry("Infinity", "Infinity", nil),
Entry("Invalid value", "INVALID VALUE", nil),
@@ -299,8 +298,8 @@ var _ = Describe("Metadata", func() {
Expect(mf.RGTrackGain).To(Equal(expected))
},
Entry("0", "0", gg.P(5.0)),
Entry("-3776", "-3776", gg.P(-9.75)),
Entry("0", "0", new(5.0)),
Entry("-3776", "-3776", new(-9.75)),
Entry("Infinity", "Infinity", nil),
Entry("Invalid value", "INVALID VALUE", nil),
)
+1 -2
View File
@@ -18,7 +18,6 @@ import (
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
. "github.com/navidrome/navidrome/utils/gg"
"github.com/navidrome/navidrome/utils/slice"
"github.com/pocketbase/dbx"
)
@@ -219,7 +218,7 @@ func (r *artistRepository) Exists(id string) (bool, error) {
func (r *artistRepository) Put(a *model.Artist, colsToUpdate ...string) error {
dba := &dbArtist{Artist: a}
dba.CreatedAt = P(time.Now())
dba.CreatedAt = new(time.Now())
dba.UpdatedAt = dba.CreatedAt
_, err := r.put(dba.ID, dba, colsToUpdate...)
return err
+1 -2
View File
@@ -14,9 +14,8 @@ type genreRepository struct {
}
func NewGenreRepository(ctx context.Context, db dbx.Builder) model.GenreRepository {
genreFilter := model.TagGenre
return &genreRepository{
baseTagRepository: newBaseTagRepository(ctx, db, &genreFilter),
baseTagRepository: newBaseTagRepository(ctx, db, new(model.TagGenre)),
}
}
+4 -10
View File
@@ -13,7 +13,6 @@ import (
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/tests"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/pocketbase/dbx"
@@ -103,7 +102,7 @@ var (
songAntenna = mf(model.MediaFile{ID: "1004", Title: "Antenna", ArtistID: "2", Artist: "Kraftwerk",
AlbumID: "103",
Path: p("kraft/radio/antenna.mp3"),
RGAlbumGain: gg.P(1.0), RGAlbumPeak: gg.P(2.0), RGTrackGain: gg.P(3.0), RGTrackPeak: gg.P(4.0),
RGAlbumGain: new(1.0), RGAlbumPeak: new(2.0), RGTrackGain: new(3.0), RGTrackPeak: new(4.0),
})
songAntennaWithLyrics = mf(model.MediaFile{
ID: "1005",
@@ -162,8 +161,6 @@ func p(path string) string {
return filepath.FromSlash(path)
}
// Initialize test DB
// TODO Load this data setup from file(s)
var _ = BeforeSuite(func() {
conn := GetDBXBuilder()
ctx := log.NewContext(context.TODO())
@@ -187,8 +184,7 @@ var _ = BeforeSuite(func() {
alr := NewAlbumRepository(ctx, conn).(*albumRepository)
for i := range testAlbums {
a := testAlbums[i]
err := alr.Put(&a)
err := alr.Put(new(testAlbums[i]))
if err != nil {
panic(err)
}
@@ -196,8 +192,7 @@ var _ = BeforeSuite(func() {
arr := NewArtistRepository(ctx, conn)
for i := range testArtists {
a := testArtists[i]
err := arr.Put(&a)
err := arr.Put(new(testArtists[i]))
if err != nil {
panic(err)
}
@@ -243,8 +238,7 @@ var _ = BeforeSuite(func() {
rar := NewRadioRepository(ctx, conn)
for i := range testRadios {
r := testRadios[i]
err := rar.Put(&r)
err := rar.Put(new(testRadios[i]))
if err != nil {
panic(err)
}
+1 -2
View File
@@ -89,8 +89,7 @@ func (r *playQueueRepository) Retrieve(userId string) (*model.PlayQueue, error)
sel := r.newSelect().Columns("*").Where(Eq{"user_id": userId})
var res playQueue
err := r.queryOne(sel, &res)
q := r.toModel(&res)
return &q, err
return new(r.toModel(&res)), err
}
func (r *playQueueRepository) fromModel(q *model.PlayQueue) playQueue {
+1 -2
View File
@@ -34,8 +34,7 @@ var _ = Describe("RadioRepository", func() {
}
for i := range testRadios {
r := testRadios[i]
err := repo.Put(&r)
err := repo.Put(new(testRadios[i]))
if err != nil {
panic(err)
}
+3 -6
View File
@@ -37,8 +37,7 @@ var _ = Describe("KVStoreService", func() {
conf.Server.DataFolder = conf.NewDir(tmpDir)
// Create service with 1KB limit for testing
maxSize := "1KB"
service, err = newKVStoreService(ctx, "test_plugin", &KVStorePermission{MaxSize: &maxSize})
service, err = newKVStoreService(ctx, "test_plugin", &KVStorePermission{MaxSize: new("1KB")})
Expect(err).ToNot(HaveOccurred())
})
@@ -253,8 +252,7 @@ var _ = Describe("KVStoreService", func() {
// Close and reopen the service (simulating restart)
Expect(service.Close()).To(Succeed())
maxSize := "1KB"
service2, err := newKVStoreService(ctx, "test_plugin", &KVStorePermission{MaxSize: &maxSize})
service2, err := newKVStoreService(ctx, "test_plugin", &KVStorePermission{MaxSize: new("1KB")})
Expect(err).ToNot(HaveOccurred())
defer service2.Close()
@@ -452,8 +450,7 @@ var _ = Describe("KVStoreService", func() {
closeCtx, closeCancel := context.WithCancel(ctx)
defer closeCancel()
maxSize := "1KB"
svc, err := newKVStoreService(closeCtx, "test_close_race", &KVStorePermission{MaxSize: &maxSize})
svc, err := newKVStoreService(closeCtx, "test_close_race", &KVStorePermission{MaxSize: new("1KB")})
Expect(err).ToNot(HaveOccurred())
// Insert an expired key so cleanup has work to do
+15 -30
View File
@@ -35,8 +35,7 @@ var _ = Describe("LibraryService", Ordered, func() {
Describe("GetLibrary", func() {
It("should return library metadata without filesystem permission", func() {
reason := "test"
service = newLibraryService(ds, &LibraryPermission{Reason: &reason, Filesystem: false}, nil, true).(*libraryServiceImpl)
service = newLibraryService(ds, &LibraryPermission{Reason: new("test"), Filesystem: false}, nil, true).(*libraryServiceImpl)
lib := &model.Library{
ID: 1,
@@ -67,8 +66,7 @@ var _ = Describe("LibraryService", Ordered, func() {
})
It("should return library metadata with filesystem permission", func() {
reason := "test"
service = newLibraryService(ds, &LibraryPermission{Reason: &reason, Filesystem: true}, nil, true).(*libraryServiceImpl)
service = newLibraryService(ds, &LibraryPermission{Reason: new("test"), Filesystem: true}, nil, true).(*libraryServiceImpl)
lib := &model.Library{
ID: 2,
@@ -93,8 +91,7 @@ var _ = Describe("LibraryService", Ordered, func() {
})
It("should return error for non-existent library", func() {
reason := "test"
service = newLibraryService(ds, &LibraryPermission{Reason: &reason}, nil, true).(*libraryServiceImpl)
service = newLibraryService(ds, &LibraryPermission{Reason: new("test")}, nil, true).(*libraryServiceImpl)
mockLibRepo := ds.Library(ctx).(*tests.MockLibraryRepo)
mockLibRepo.SetData(model.Libraries{})
@@ -107,8 +104,7 @@ var _ = Describe("LibraryService", Ordered, func() {
Describe("GetAllLibraries", func() {
It("should return all libraries without filesystem permission", func() {
reason := "test"
service = newLibraryService(ds, &LibraryPermission{Reason: &reason, Filesystem: false}, nil, true).(*libraryServiceImpl)
service = newLibraryService(ds, &LibraryPermission{Reason: new("test"), Filesystem: false}, nil, true).(*libraryServiceImpl)
libs := model.Libraries{
{ID: 1, Name: "Rock", Path: "/music/rock", TotalSongs: 100},
@@ -130,8 +126,7 @@ var _ = Describe("LibraryService", Ordered, func() {
})
It("should return all libraries with filesystem permission", func() {
reason := "test"
service = newLibraryService(ds, &LibraryPermission{Reason: &reason, Filesystem: true}, nil, true).(*libraryServiceImpl)
service = newLibraryService(ds, &LibraryPermission{Reason: new("test"), Filesystem: true}, nil, true).(*libraryServiceImpl)
libs := model.Libraries{
{ID: 1, Name: "Rock", Path: "/music/rock", TotalSongs: 100},
@@ -152,10 +147,8 @@ var _ = Describe("LibraryService", Ordered, func() {
})
Describe("Library Access Filtering", func() {
It("should only return libraries in the allowed list", func() {
reason := "test"
// Only allow library ID 2
service = newLibraryService(ds, &LibraryPermission{Reason: &reason, Filesystem: false}, []int{2}, false).(*libraryServiceImpl)
It("should only return libraries in the allowed list", func() { // Only allow library ID 2
service = newLibraryService(ds, &LibraryPermission{Reason: new("test"), Filesystem: false}, []int{2}, false).(*libraryServiceImpl)
libs := model.Libraries{
{ID: 1, Name: "Rock", Path: "/music/rock", TotalSongs: 100},
@@ -173,10 +166,8 @@ var _ = Describe("LibraryService", Ordered, func() {
Expect(results[0].Name).To(Equal("Jazz"))
})
It("should return error when getting a library not in the allowed list", func() {
reason := "test"
// Only allow library ID 2
service = newLibraryService(ds, &LibraryPermission{Reason: &reason, Filesystem: false}, []int{2}, false).(*libraryServiceImpl)
It("should return error when getting a library not in the allowed list", func() { // Only allow library ID 2
service = newLibraryService(ds, &LibraryPermission{Reason: new("test"), Filesystem: false}, []int{2}, false).(*libraryServiceImpl)
libs := model.Libraries{
{ID: 1, Name: "Rock", Path: "/music/rock", TotalSongs: 100},
@@ -192,10 +183,8 @@ var _ = Describe("LibraryService", Ordered, func() {
Expect(err.Error()).To(ContainSubstring("not accessible"))
})
It("should allow access to a library in the allowed list", func() {
reason := "test"
// Only allow library ID 2
service = newLibraryService(ds, &LibraryPermission{Reason: &reason, Filesystem: false}, []int{2}, false).(*libraryServiceImpl)
It("should allow access to a library in the allowed list", func() { // Only allow library ID 2
service = newLibraryService(ds, &LibraryPermission{Reason: new("test"), Filesystem: false}, []int{2}, false).(*libraryServiceImpl)
libs := model.Libraries{
{ID: 1, Name: "Rock", Path: "/music/rock", TotalSongs: 100},
@@ -211,10 +200,8 @@ var _ = Describe("LibraryService", Ordered, func() {
Expect(result.Name).To(Equal("Jazz"))
})
It("should return empty list when no libraries are allowed and allLibraries is false", func() {
reason := "test"
// No libraries allowed
service = newLibraryService(ds, &LibraryPermission{Reason: &reason, Filesystem: false}, []int{}, false).(*libraryServiceImpl)
It("should return empty list when no libraries are allowed and allLibraries is false", func() { // No libraries allowed
service = newLibraryService(ds, &LibraryPermission{Reason: new("test"), Filesystem: false}, []int{}, false).(*libraryServiceImpl)
libs := model.Libraries{
{ID: 1, Name: "Rock", Path: "/music/rock", TotalSongs: 100},
@@ -229,10 +216,8 @@ var _ = Describe("LibraryService", Ordered, func() {
Expect(results).To(HaveLen(0))
})
It("should return all libraries when allLibraries is true regardless of allowed list", func() {
reason := "test"
// allLibraries=true should ignore the allowed list
service = newLibraryService(ds, &LibraryPermission{Reason: &reason, Filesystem: false}, []int{1}, true).(*libraryServiceImpl)
It("should return all libraries when allLibraries is true regardless of allowed list", func() { // allLibraries=true should ignore the allowed list
service = newLibraryService(ds, &LibraryPermission{Reason: new("test"), Filesystem: false}, []int{1}, true).(*libraryServiceImpl)
libs := model.Libraries{
{ID: 1, Name: "Rock", Path: "/music/rock", TotalSongs: 100},
+1 -2
View File
@@ -302,8 +302,7 @@ func (s *webSocketServiceImpl) readLoop(ctx context.Context, connectionID string
if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived) {
closeCode := websocket.CloseNoStatusReceived
closeReason := ""
var ce *websocket.CloseError
if errors.As(err, &ce) {
if ce, ok := errors.AsType[*websocket.CloseError](err); ok {
closeCode = ce.Code
closeReason = ce.Text
}
+1 -2
View File
@@ -140,11 +140,10 @@ var _ = Describe("Manifest", func() {
})
It("returns true when threads feature has a reason", func() {
reason := "Required for concurrent processing"
m := &Manifest{
Experimental: &Experimental{
Threads: &ThreadsFeature{
Reason: &reason,
Reason: new("Required for concurrent processing"),
},
},
}
+1 -2
View File
@@ -135,12 +135,11 @@ var _ = Describe("ndpPackage", func() {
Describe("readManifest", func() {
It("should read only the manifest without loading wasm", func() {
ndpPath := filepath.Join(tmpDir, "test.ndp")
desc := "A test plugin"
manifest := &Manifest{
Name: "Test Plugin",
Author: "Test Author",
Version: "1.0.0",
Description: &desc,
Description: new("A test plugin"),
}
wasmBytes := make([]byte, 1024*1024) // 1MB of zeros
+1 -2
View File
@@ -17,7 +17,6 @@ import (
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/server/events"
. "github.com/navidrome/navidrome/utils/gg"
"github.com/navidrome/navidrome/utils/pl"
"golang.org/x/time/rate"
)
@@ -38,7 +37,7 @@ func New(rootCtx context.Context, ds model.DataStore, cw artwork.CacheWarmer, br
devExternalScanner: conf.Server.DevExternalScanner,
}
if !c.devExternalScanner {
c.limiter = P(rate.Sometimes{Interval: conf.Server.DevActivityPanelUpdateRate})
c.limiter = new(rate.Sometimes{Interval: conf.Server.DevActivityPanelUpdateRate})
}
return c
}
+1 -2
View File
@@ -97,8 +97,7 @@ func (s *scannerExternal) scan(ctx context.Context, fullScan bool, targets []mod
func (s *scannerExternal) wait(cmd *exec.Cmd, out *io.PipeWriter) {
if err := cmd.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok {
_ = out.CloseWithError(fmt.Errorf("%s exited with non-zero status code: %w", cmd, exitErr))
} else {
_ = out.CloseWithError(fmt.Errorf("waiting %s cmd: %w", cmd, err))
+1 -2
View File
@@ -135,7 +135,6 @@ func createAdmin(ds model.DataStore) func(w http.ResponseWriter, r *http.Request
func createAdminUser(ctx context.Context, ds model.DataStore, username, password string) error {
log.Warn(ctx, "Creating initial user", "user", username)
now := time.Now()
caser := cases.Title(language.Und)
initialUser := model.User{
ID: id.NewRandom(),
@@ -144,7 +143,7 @@ func createAdminUser(ctx context.Context, ds model.DataStore, username, password
Email: "",
NewPassword: password,
IsAdmin: true,
LastLoginAt: &now,
LastLoginAt: new(time.Now()),
}
err := ds.User(ctx).Put(&initialUser)
if err != nil {
+2 -4
View File
@@ -45,8 +45,7 @@ func (api *Router) uploadArtistImage() http.HandlerFunc {
return err
}
ar.UploadedImage = filename
now := time.Now()
ar.UpdatedAt = &now
ar.UpdatedAt = new(time.Now())
return api.ds.Artist(ctx).Put(ar, "uploaded_image", "updated_at")
})
}
@@ -65,8 +64,7 @@ func (api *Router) deleteArtistImage() http.HandlerFunc {
return err
}
ar.UploadedImage = ""
now := time.Now()
ar.UpdatedAt = &now
ar.UpdatedAt = new(time.Now())
return api.ds.Artist(ctx).Put(ar, "uploaded_image", "updated_at")
})
}
+11 -12
View File
@@ -9,7 +9,6 @@ import (
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/tests"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -32,7 +31,7 @@ var _ = Describe("Queue Endpoints", func() {
Describe("POST /queue", func() {
It("saves the queue", func() {
payload := updateQueuePayload{Ids: gg.P([]string{"s1", "s2"}), Current: gg.P(1), Position: gg.P(int64(10))}
payload := updateQueuePayload{Ids: new([]string{"s1", "s2"}), Current: new(1), Position: new(int64(10))}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("POST", "/queue", bytes.NewReader(body))
ctx := request.WithUser(req.Context(), user)
@@ -50,7 +49,7 @@ var _ = Describe("Queue Endpoints", func() {
})
It("saves an empty queue", func() {
payload := updateQueuePayload{Ids: gg.P([]string{}), Current: gg.P(0), Position: gg.P(int64(0))}
payload := updateQueuePayload{Ids: new([]string{}), Current: new(0), Position: new(int64(0))}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("POST", "/queue", bytes.NewReader(body))
req = req.WithContext(request.WithUser(req.Context(), user))
@@ -63,7 +62,7 @@ var _ = Describe("Queue Endpoints", func() {
})
It("returns bad request for invalid current index (negative)", func() {
payload := updateQueuePayload{Ids: gg.P([]string{"s1", "s2"}), Current: gg.P(-1), Position: gg.P(int64(10))}
payload := updateQueuePayload{Ids: new([]string{"s1", "s2"}), Current: new(-1), Position: new(int64(10))}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("POST", "/queue", bytes.NewReader(body))
req = req.WithContext(request.WithUser(req.Context(), user))
@@ -75,7 +74,7 @@ var _ = Describe("Queue Endpoints", func() {
})
It("returns bad request for invalid current index (too large)", func() {
payload := updateQueuePayload{Ids: gg.P([]string{"s1", "s2"}), Current: gg.P(2), Position: gg.P(int64(10))}
payload := updateQueuePayload{Ids: new([]string{"s1", "s2"}), Current: new(2), Position: new(int64(10))}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("POST", "/queue", bytes.NewReader(body))
req = req.WithContext(request.WithUser(req.Context(), user))
@@ -97,7 +96,7 @@ var _ = Describe("Queue Endpoints", func() {
It("returns internal server error when store fails", func() {
repo.Err = true
payload := updateQueuePayload{Ids: gg.P([]string{"s1"}), Current: gg.P(0), Position: gg.P(int64(10))}
payload := updateQueuePayload{Ids: new([]string{"s1"}), Current: new(0), Position: new(int64(10))}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("POST", "/queue", bytes.NewReader(body))
req = req.WithContext(request.WithUser(req.Context(), user))
@@ -166,7 +165,7 @@ var _ = Describe("Queue Endpoints", func() {
Describe("PUT /queue", func() {
It("updates the queue fields", func() {
repo.Queue = &model.PlayQueue{UserID: user.ID, Items: model.MediaFiles{{ID: "s1"}, {ID: "s2"}, {ID: "s3"}}}
payload := updateQueuePayload{Current: gg.P(2), Position: gg.P(int64(20))}
payload := updateQueuePayload{Current: new(2), Position: new(int64(20))}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("PUT", "/queue", bytes.NewReader(body))
ctx := request.WithUser(req.Context(), user)
@@ -184,7 +183,7 @@ var _ = Describe("Queue Endpoints", func() {
It("updates only ids", func() {
repo.Queue = &model.PlayQueue{UserID: user.ID, Current: 1}
payload := updateQueuePayload{Ids: gg.P([]string{"s1", "s2"})}
payload := updateQueuePayload{Ids: new([]string{"s1", "s2"})}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("PUT", "/queue", bytes.NewReader(body))
req = req.WithContext(request.WithUser(req.Context(), user))
@@ -198,7 +197,7 @@ var _ = Describe("Queue Endpoints", func() {
It("updates ids and current", func() {
repo.Queue = &model.PlayQueue{UserID: user.ID}
payload := updateQueuePayload{Ids: gg.P([]string{"s1", "s2"}), Current: gg.P(1)}
payload := updateQueuePayload{Ids: new([]string{"s1", "s2"}), Current: new(1)}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("PUT", "/queue", bytes.NewReader(body))
req = req.WithContext(request.WithUser(req.Context(), user))
@@ -213,7 +212,7 @@ var _ = Describe("Queue Endpoints", func() {
It("returns bad request when new ids invalidate current", func() {
repo.Queue = &model.PlayQueue{UserID: user.ID, Current: 2}
payload := updateQueuePayload{Ids: gg.P([]string{"s1", "s2"})}
payload := updateQueuePayload{Ids: new([]string{"s1", "s2"})}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("PUT", "/queue", bytes.NewReader(body))
req = req.WithContext(request.WithUser(req.Context(), user))
@@ -225,7 +224,7 @@ var _ = Describe("Queue Endpoints", func() {
It("returns bad request when current out of bounds", func() {
repo.Queue = &model.PlayQueue{UserID: user.ID, Items: model.MediaFiles{{ID: "s1"}}}
payload := updateQueuePayload{Current: gg.P(3)}
payload := updateQueuePayload{Current: new(3)}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("PUT", "/queue", bytes.NewReader(body))
req = req.WithContext(request.WithUser(req.Context(), user))
@@ -246,7 +245,7 @@ var _ = Describe("Queue Endpoints", func() {
It("returns internal server error when store fails", func() {
repo.Err = true
payload := updateQueuePayload{Position: gg.P(int64(10))}
payload := updateQueuePayload{Position: new(int64(10))}
body, _ := json.Marshal(payload)
req := httptest.NewRequest("PUT", "/queue", bytes.NewReader(body))
req = req.WithContext(request.WithUser(req.Context(), user))
+2 -4
View File
@@ -12,7 +12,6 @@ import (
"github.com/navidrome/navidrome/core/stream"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
. "github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -89,7 +88,7 @@ var _ = Describe("encodeMediafileShare", func() {
})
It("includes the share ID in the token", func() {
exp := P(time.Now().Add(time.Hour))
exp := new(time.Now().Add(time.Hour))
s := model.Share{ID: "shareABC", Format: "mp3", MaxBitRate: 320, ExpiresAt: exp}
token := encodeMediafileShare(s, "mf-999")
info, err := decodeStreamInfo(token)
@@ -164,8 +163,7 @@ var _ = Describe("handleStream", func() {
It("returns 410 when share has been set to expired", func() {
shareRepo.ID = "share123"
expired := time.Now().Add(-time.Hour)
shareRepo.Entity = &model.Share{ID: "share123", ExpiresAt: &expired}
shareRepo.Entity = &model.Share{ID: "share123", ExpiresAt: new(time.Now().Add(-time.Hour))}
claims := auth.Claims{ID: "mf-123", ShareID: "share123"}
token, _ := auth.CreatePublicToken(claims)
+1 -2
View File
@@ -9,7 +9,6 @@ import (
"strings"
"github.com/navidrome/navidrome/server/subsonic/responses"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"golang.org/x/net/context"
@@ -136,7 +135,7 @@ var _ = Describe("sendResponse", func() {
It("should return a fail response", func() {
payload.Song = &responses.Child{OpenSubsonicChild: &responses.OpenSubsonicChild{}}
// An +Inf value will cause an error when marshalling to JSON
payload.Song.ReplayGain = responses.ReplayGain{TrackGain: gg.P(math.Inf(1))}
payload.Song.ReplayGain = responses.ReplayGain{TrackGain: new(math.Inf(1))}
q := r.URL.Query()
q.Add("f", "json")
r.URL.RawQuery = q.Encode()
+1 -2
View File
@@ -256,8 +256,7 @@ func (api *Router) GetSong(r *http.Request) (*responses.Subsonic, error) {
}
response := newResponse()
child := childFromMediaFile(ctx, *mf)
response.Song = &child
response.Song = new(childFromMediaFile(ctx, *mf))
return response, nil
}
+3 -4
View File
@@ -18,7 +18,6 @@ import (
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/server/subsonic/responses"
. "github.com/navidrome/navidrome/utils/gg"
"github.com/navidrome/navidrome/utils/number"
"github.com/navidrome/navidrome/utils/req"
"github.com/navidrome/navidrome/utils/slice"
@@ -217,7 +216,7 @@ func childFromMediaFile(ctx context.Context, mf model.MediaFile) responses.Child
child.Path = fakePath(mf)
}
child.DiscNumber = int32(mf.DiscNumber)
child.Created = P(mf.BirthTime)
child.Created = new(mf.BirthTime)
child.AlbumId = mf.AlbumID
child.ArtistId = mf.ArtistID
child.Type = "music"
@@ -346,7 +345,7 @@ func childFromAlbum(ctx context.Context, al model.Album) responses.Child {
child.Year = int32(cmp.Or(al.MaxOriginalYear, al.MaxYear))
child.Genre = al.Genre
child.CoverArt = al.CoverArtID().String()
child.Created = P(albumCreatedAt(al))
child.Created = new(albumCreatedAt(al))
child.Parent = al.AlbumArtistID
child.ArtistId = al.AlbumArtistID
child.Duration = int32(al.Duration)
@@ -442,7 +441,7 @@ func buildAlbumID3(ctx context.Context, album model.Album) responses.AlbumID3 {
dir.PlayCount = album.PlayCount
dir.Year = int32(cmp.Or(album.MaxOriginalYear, album.MaxYear))
dir.Genre = album.Genre
dir.Created = P(albumCreatedAt(album))
dir.Created = new(albumCreatedAt(album))
if album.Starred {
dir.Starred = album.StarredAt
}
+1 -2
View File
@@ -294,7 +294,6 @@ var _ = Describe("MediaRetrievalController", func() {
response, err := router.GetLyricsBySongId(r)
Expect(err).ToNot(HaveOccurred())
offset := int64(-100)
compareResponses(response.LyricsList, responses.LyricsList{
StructuredLyrics: responses.StructuredLyrics{
{
@@ -312,7 +311,7 @@ var _ = Describe("MediaRetrievalController", func() {
Value: "You know the rules and so do I",
},
},
Offset: &offset,
Offset: new(int64(-100)),
},
},
})
+1 -2
View File
@@ -12,7 +12,6 @@ import (
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/server/subsonic/responses"
. "github.com/navidrome/navidrome/utils/gg"
"github.com/navidrome/navidrome/utils/req"
"github.com/navidrome/navidrome/utils/slice"
)
@@ -169,7 +168,7 @@ func buildOSPlaylist(ctx context.Context, p model.Playlist) *responses.OpenSubso
pls.Readonly = true
if p.EvaluatedAt != nil {
pls.ValidUntil = P(p.EvaluatedAt.Add(conf.Server.SmartPlaylistRefreshDelay))
pls.ValidUntil = new(p.EvaluatedAt.Add(conf.Server.SmartPlaylistRefreshDelay))
}
} else {
user, ok := request.UserFrom(ctx)
+9 -14
View File
@@ -8,7 +8,6 @@ import (
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/server/subsonic/responses"
. "github.com/navidrome/navidrome/server/subsonic/responses"
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -94,11 +93,10 @@ var _ = Describe("Responses", func() {
Context("with data", func() {
BeforeEach(func() {
artists := make([]Artist, 1)
t := time.Date(2016, 03, 2, 20, 30, 0, 0, time.UTC)
artists[0] = Artist{
Id: "111",
Name: "aaa",
Starred: &t,
Starred: new(time.Date(2016, 03, 2, 20, 30, 0, 0, time.UTC)),
UserRating: 3,
ArtistImageUrl: "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png",
}
@@ -133,11 +131,10 @@ var _ = Describe("Responses", func() {
Context("with data", func() {
BeforeEach(func() {
artists := make([]ArtistID3, 1)
t := time.Date(2016, 03, 2, 20, 30, 0, 0, time.UTC)
artists[0] = ArtistID3{
Id: "111",
Name: "aaa",
Starred: &t,
Starred: new(time.Date(2016, 03, 2, 20, 30, 0, 0, time.UTC)),
UserRating: 3,
AlbumCount: 2,
ArtistImageUrl: "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png",
@@ -158,11 +155,10 @@ var _ = Describe("Responses", func() {
Context("with OpenSubsonic data", func() {
BeforeEach(func() {
artists := make([]ArtistID3, 1)
t := time.Date(2016, 03, 2, 20, 30, 0, 0, time.UTC)
artists[0] = ArtistID3{
Id: "111",
Name: "aaa",
Starred: &t,
Starred: new(time.Date(2016, 03, 2, 20, 30, 0, 0, time.UTC)),
UserRating: 3,
AlbumCount: 2,
ArtistImageUrl: "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png",
@@ -211,12 +207,11 @@ var _ = Describe("Responses", func() {
BeforeEach(func() {
response.Directory = &Directory{Id: "1", Name: "N"}
child := make([]Child, 2)
t := time.Date(2016, 03, 2, 20, 30, 0, 0, time.UTC)
child[0] = Child{
Id: "1", IsDir: true, Title: "title", Album: "album", Artist: "artist", Track: 1,
Year: 1985, Genre: "Rock", CoverArt: "1", Size: 8421341, ContentType: "audio/flac",
Suffix: "flac", TranscodedContentType: "audio/mpeg", TranscodedSuffix: "mp3",
Duration: 146, BitRate: 320, Starred: &t,
Duration: 146, BitRate: 320, Starred: new(time.Date(2016, 03, 2, 20, 30, 0, 0, time.UTC)),
}
child[0].OpenSubsonicChild = &OpenSubsonicChild{
Genres: []ItemGenre{{Name: "rock"}, {Name: "progressive"}},
@@ -225,7 +220,7 @@ var _ = Describe("Responses", func() {
BPM: 127, ChannelCount: 2, SamplingRate: 44100, BitDepth: 16,
Moods: []string{"happy", "sad"},
Groupings: []string{"Soundtrack", "Live"},
ReplayGain: ReplayGain{TrackGain: gg.P(1.0), AlbumGain: gg.P(2.0), TrackPeak: gg.P(3.0), AlbumPeak: gg.P(4.0), BaseGain: gg.P(5.0), FallbackGain: gg.P(6.0)},
ReplayGain: ReplayGain{TrackGain: new(1.0), AlbumGain: new(2.0), TrackPeak: new(3.0), AlbumPeak: new(4.0), BaseGain: new(5.0), FallbackGain: new(6.0)},
DisplayArtist: "artist 1 & artist 2",
Artists: []ArtistID3Ref{
{Id: "1", Name: "artist1"},
@@ -246,7 +241,7 @@ var _ = Describe("Responses", func() {
ExplicitStatus: "clean",
}
child[1].OpenSubsonicChild = &OpenSubsonicChild{
ReplayGain: ReplayGain{TrackGain: gg.P(0.0), AlbumGain: gg.P(0.0), TrackPeak: gg.P(0.0), AlbumPeak: gg.P(0.0), BaseGain: gg.P(0.0), FallbackGain: gg.P(0.0)},
ReplayGain: ReplayGain{TrackGain: new(0.0), AlbumGain: new(0.0), TrackPeak: new(0.0), AlbumPeak: new(0.0), BaseGain: new(0.0), FallbackGain: new(0.0)},
}
response.Directory.Child = child
})
@@ -322,7 +317,7 @@ var _ = Describe("Responses", func() {
Isrc: []string{"ISRC-1"},
Moods: []string{"happy", "sad"},
Groupings: []string{"Soundtrack", "Live"},
ReplayGain: ReplayGain{TrackGain: gg.P(1.0), AlbumGain: gg.P(2.0), TrackPeak: gg.P(3.0), AlbumPeak: gg.P(4.0), BaseGain: gg.P(5.0), FallbackGain: gg.P(6.0)},
ReplayGain: ReplayGain{TrackGain: new(1.0), AlbumGain: new(2.0), TrackPeak: new(3.0), AlbumPeak: new(4.0), BaseGain: new(5.0), FallbackGain: new(6.0)},
BPM: 127, ChannelCount: 2, SamplingRate: 44100, BitDepth: 16,
DisplayArtist: "artist1 & artist2",
Artists: []ArtistID3Ref{
@@ -342,7 +337,7 @@ var _ = Describe("Responses", func() {
ExplicitStatus: "clean",
}
songs[1].OpenSubsonicChild = &OpenSubsonicChild{
ReplayGain: ReplayGain{TrackGain: gg.P(0.0), AlbumGain: gg.P(0.0), TrackPeak: gg.P(0.0), AlbumPeak: gg.P(0.0), BaseGain: gg.P(0.0), FallbackGain: gg.P(0.0)},
ReplayGain: ReplayGain{TrackGain: new(0.0), AlbumGain: new(0.0), TrackPeak: new(0.0), AlbumPeak: new(0.0), BaseGain: new(0.0), FallbackGain: new(0.0)},
}
response.AlbumWithSongsID3.AlbumID3 = album
response.AlbumWithSongsID3.Song = songs
@@ -804,7 +799,7 @@ var _ = Describe("Responses", func() {
Context("with data", func() {
BeforeEach(func() {
response.PlayQueueByIndex.Username = "user1"
response.PlayQueueByIndex.CurrentIndex = gg.P(0)
response.PlayQueueByIndex.CurrentIndex = new(0)
response.PlayQueueByIndex.Position = 243
response.PlayQueueByIndex.Changed = time.Time{}
response.PlayQueueByIndex.ChangedBy = "a_client"
+2 -6
View File
@@ -58,12 +58,10 @@ func (api *Router) CreateShare(r *http.Request) (*responses.Subsonic, error) {
}
description, _ := p.String("description")
expires := p.TimeOr("expires", time.Time{})
repo := api.share.NewRepository(r.Context())
share := &model.Share{
Description: description,
ExpiresAt: &expires,
ExpiresAt: new(p.TimeOr("expires", time.Time{})),
ResourceIDs: strings.Join(ids, ","),
}
@@ -90,13 +88,11 @@ func (api *Router) UpdateShare(r *http.Request) (*responses.Subsonic, error) {
}
description, _ := p.String("description")
expires := p.TimeOr("expires", time.Time{})
repo := api.share.NewRepository(r.Context())
share := &model.Share{
ID: id,
Description: description,
ExpiresAt: &expires,
ExpiresAt: new(p.TimeOr("expires", time.Time{})),
}
err = repo.(rest.Persistable).Update(id, share)
+1 -2
View File
@@ -46,8 +46,7 @@ func (api *Router) GetUser(r *http.Request) (*responses.Subsonic, error) {
return nil, newError(responses.ErrorAuthorizationFail)
}
response := newResponse()
user := buildUserResponse(loggedUser)
response.User = &user
response.User = new(buildUserResponse(loggedUser))
return response, nil
}
+2 -3
View File
@@ -7,7 +7,6 @@ import (
"time"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils/gg"
)
func CreateMockUserRepo() *MockedUserRepo {
@@ -84,7 +83,7 @@ func (u *MockedUserRepo) GetAll(options ...model.QueryOptions) (model.Users, err
func (u *MockedUserRepo) UpdateLastLoginAt(id string) error {
for _, usr := range u.Data {
if usr.ID == id {
usr.LastLoginAt = gg.P(time.Now())
usr.LastLoginAt = new(time.Now())
return nil
}
}
@@ -94,7 +93,7 @@ func (u *MockedUserRepo) UpdateLastLoginAt(id string) error {
func (u *MockedUserRepo) UpdateLastAccessAt(id string) error {
for _, usr := range u.Data {
if usr.ID == id {
usr.LastAccessAt = gg.P(time.Now())
usr.LastAccessAt = new(time.Now())
return nil
}
}
+1 -2
View File
@@ -75,8 +75,7 @@ func (c *HTTPClient) serializeReq(req *http.Request) string {
}
if req.Body != nil {
bodyData, _ := io.ReadAll(req.Body)
bodyStr := base64.StdEncoding.EncodeToString(bodyData)
data.Body = &bodyStr
data.Body = new(base64.StdEncoding.EncodeToString(bodyData))
}
j, _ := json.Marshal(&data)
return string(j)
+1 -2
View File
@@ -9,7 +9,6 @@ import (
"time"
"github.com/jellydator/ttlcache/v3"
. "github.com/navidrome/navidrome/utils/gg"
)
type SimpleCache[K comparable, V any] interface {
@@ -119,7 +118,7 @@ func (c *simpleCache[K, V]) GetWithLoader(key K, loader func(key K) (V, time.Dur
func (c *simpleCache[K, V]) evictExpired() {
if c.evictionDeadline.Load() == nil || c.evictionDeadline.Load().Before(time.Now()) {
c.data.DeleteExpired()
c.evictionDeadline.Store(P(time.Now().Add(evictionTimeout)))
c.evictionDeadline.Store(new(time.Now().Add(evictionTimeout)))
}
}
+1 -3
View File
@@ -2,8 +2,6 @@ package chrono
import (
"time"
. "github.com/navidrome/navidrome/utils/gg"
)
// Meter is a simple stopwatch
@@ -13,7 +11,7 @@ type Meter struct {
}
func (m *Meter) Start() {
m.mark = P(time.Now())
m.mark = new(time.Now())
}
func (m *Meter) Stop() time.Duration {
-5
View File
@@ -1,11 +1,6 @@
// Package gg implements simple "extensions" to Go language. Based on https://github.com/icza/gog
package gg
// P returns a pointer to the input value
func P[T any](v T) *T {
return &v
}
// V returns the value of the input pointer, or a zero value if the input pointer is nil.
func V[T any](p *T) T {
if p == nil {
+1 -14
View File
@@ -16,22 +16,9 @@ func TestGG(t *testing.T) {
}
var _ = Describe("GG", func() {
Describe("P", func() {
It("returns a pointer to the input value", func() {
v := 123
Expect(gg.P(123)).To(Equal(&v))
})
It("returns nil if the input value is zero", func() {
v := 0
Expect(gg.P(0)).To(Equal(&v))
})
})
Describe("V", func() {
It("returns the value of the input pointer", func() {
v := 123
Expect(gg.V(&v)).To(Equal(123))
Expect(gg.V(new(123))).To(Equal(123))
})
It("returns a zero value if the input pointer is nil", func() {
+2 -4
View File
@@ -38,8 +38,7 @@ func (r *Values) String(param string) (string, error) {
func (r *Values) StringPtr(param string) *string {
var v *string
if _, exists := r.URL.Query()[param]; exists {
s := r.URL.Query().Get(param)
v = &s
v = new(r.URL.Query().Get(param))
}
return v
}
@@ -48,8 +47,7 @@ func (r *Values) BoolPtr(param string) *bool {
var v *bool
if _, exists := r.URL.Query()[param]; exists {
s := r.URL.Query().Get(param)
b := strings.Contains("/true/on/1/", "/"+strings.ToLower(s)+"/")
v = &b
v = new(strings.Contains("/true/on/1/", "/"+strings.ToLower(s)+"/"))
}
return v
}