navidrome/server/jellyfin/api.go
Deluan 65b92168f9 feat(jellyfin): Artists and Genres endpoints
Add /Artists, /Artists/AlbumArtists, /Genres and /MusicGenres. Artists
listing is library-scoped (defaulting to the user's accessible
libraries, narrowed by an accessible ParentId), delegating access
control to listArtists/ApplyArtistLibraryFilter. Genres are global and
unscoped, matching the ML decision already made for listGenres.
2026-07-05 16:10:07 -04:00

81 lines
2.5 KiB
Go

package jellyfin
import (
"encoding/json"
"net/http"
"sync"
"github.com/go-chi/chi/v5"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/core/artwork"
"github.com/navidrome/navidrome/core/playlists"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/core/stream"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
)
type Router struct {
http.Handler
ds model.DataStore
artwork artwork.Artwork
streamer stream.MediaStreamer
transcodeDecider stream.TranscodeDecider
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,
transcodeDecider stream.TranscodeDecider, players core.Players,
scrobbler scrobbler.PlayTracker, playlists playlists.Playlists) *Router {
r := &Router{
ds: ds, artwork: artwork, streamer: streamer, transcodeDecider: transcodeDecider,
players: players, scrobbler: scrobbler, playlists: playlists,
}
r.Handler = r.routes()
return r
}
func (api *Router) routes() http.Handler {
r := chi.NewRouter()
// Public (no auth): handshake + login.
r.Get("/System/Info/Public", api.getPublicSystemInfo)
r.Get("/System/Ping", api.ping)
r.Post("/System/Ping", api.ping)
r.Get("/QuickConnect/Enabled", api.quickConnectEnabled)
r.Post("/Users/AuthenticateByName", api.authenticateByName)
r.Get("/Users/Public", api.getPublicUsers)
r.Group(func(r chi.Router) {
r.Use(api.authenticate)
r.Get("/UserViews", api.getUserViews)
r.Get("/Users/{userId}/Views", api.getUserViews)
r.Get("/Users/Me", api.getCurrentUser)
r.Get("/Users/{userId}", api.getCurrentUser)
r.Get("/Items", api.getItems)
r.Get("/Users/{userId}/Items", api.getItems)
r.Get("/Items/{itemId}", api.getItem)
r.Get("/Users/{userId}/Items/{itemId}", api.getItem)
r.Get("/Users/{userId}/Items/Latest", api.getLatest)
r.Get("/Artists", api.getArtists)
r.Get("/Artists/AlbumArtists", api.getArtists)
r.Get("/Genres", api.getGenres)
r.Get("/MusicGenres", api.getGenres)
})
return r
}
// ok writes payload as JSON.
func (api *Router) ok(w http.ResponseWriter, r *http.Request, payload any) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if err := json.NewEncoder(w).Encode(payload); err != nil {
log.Error(r.Context(), "Jellyfin API: error encoding response", err)
}
}