fix(jellyfin): memoize stable server Id and cover get-or-create path

This commit is contained in:
Deluan 2026-07-05 14:13:18 -04:00
commit 73376f519d
3 changed files with 48 additions and 10 deletions

View file

@ -3,6 +3,7 @@ package jellyfin
import (
"encoding/json"
"net/http"
"sync"
"github.com/go-chi/chi/v5"
"github.com/navidrome/navidrome/core"
@ -23,6 +24,8 @@ type Router struct {
players core.Players
scrobbler scrobbler.PlayTracker
playlists playlists.Playlists
serverIDOnce sync.Once
serverIDVal string
}
func New(ds model.DataStore, artwork artwork.Artwork, streamer stream.MediaStreamer,

View file

@ -22,18 +22,25 @@ func (api *Router) serverName() string {
// the same way core/metrics/insights.go derives its InsightsID. Jellyfin clients cache
// ServerId across sessions, so a per-process value would break re-authentication.
// api.ds is nil only in unit tests that construct Router{} directly; New() always sets it.
//
// Resolution happens once per Router (sync.Once), so concurrent first-boot requests can't
// each generate and persist a different UUID before the row settles.
func (api *Router) serverID(ctx context.Context) string {
if api.ds == nil {
return uuid.NewString()
}
id, err := api.ds.Property(ctx).Get(consts.JellyfinServerIDKey)
if err != nil {
id = uuid.NewString()
if err := api.ds.Property(ctx).Put(consts.JellyfinServerIDKey, id); err != nil {
log.Error(ctx, "Jellyfin API: could not persist server id", err)
api.serverIDOnce.Do(func() {
if api.ds == nil {
api.serverIDVal = uuid.NewString()
return
}
}
return id
id, err := api.ds.Property(ctx).Get(consts.JellyfinServerIDKey)
if err != nil {
id = uuid.NewString()
if err := api.ds.Property(ctx).Put(consts.JellyfinServerIDKey, id); err != nil {
log.Error(ctx, "Jellyfin API: could not persist server id", err)
}
}
api.serverIDVal = id
})
return api.serverIDVal
}
func (api *Router) publicInfo(ctx context.Context) dto.PublicSystemInfo {

View file

@ -1,11 +1,13 @@
package jellyfin
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"github.com/navidrome/navidrome/server/jellyfin/dto"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@ -49,4 +51,30 @@ var _ = Describe("System", func() {
Expect(json.Unmarshal(w.Body.Bytes(), &enabled)).To(Succeed())
Expect(enabled).To(BeFalse())
})
Context("serverID with a real DataStore", func() {
var ctx context.Context
var ds *tests.MockDataStore
BeforeEach(func() {
ctx = context.Background()
ds = &tests.MockDataStore{}
})
It("persists the generated id so it can be read back by another Router sharing the same DataStore", func() {
first := &Router{ds: ds}
id := first.serverID(ctx)
Expect(id).ToNot(BeEmpty())
second := &Router{ds: ds}
Expect(second.serverID(ctx)).To(Equal(id))
})
It("memoizes the id across repeated calls on the same Router", func() {
r := &Router{ds: ds}
id := r.serverID(ctx)
Expect(r.serverID(ctx)).To(Equal(id))
Expect(r.serverID(ctx)).To(Equal(id))
})
})
})