Removed list_generator completely

This commit is contained in:
Deluan 2020-10-27 10:19:58 -04:00
parent 3037ea01e2
commit 6152fadd92
11 changed files with 99 additions and 149 deletions

View file

@ -44,8 +44,6 @@ func CreateSubsonicAPIRouter() *subsonic.Router {
dataStore := persistence.New() dataStore := persistence.New()
artworkCache := core.NewImageCache() artworkCache := core.NewImageCache()
artwork := core.NewArtwork(dataStore, artworkCache) artwork := core.NewArtwork(dataStore, artworkCache)
nowPlayingRepository := engine.NewNowPlayingRepository()
listGenerator := engine.NewListGenerator(dataStore, nowPlayingRepository)
playlists := engine.NewPlaylists(dataStore) playlists := engine.NewPlaylists(dataStore)
transcoderTranscoder := transcoder.New() transcoderTranscoder := transcoder.New()
transcodingCache := core.NewTranscodingCache() transcodingCache := core.NewTranscodingCache()
@ -55,7 +53,7 @@ func CreateSubsonicAPIRouter() *subsonic.Router {
client := core.LastFMNewClient() client := core.LastFMNewClient()
spotifyClient := core.SpotifyNewClient() spotifyClient := core.SpotifyNewClient()
externalInfo := core.NewExternalInfo(dataStore, client, spotifyClient) externalInfo := core.NewExternalInfo(dataStore, client, spotifyClient)
router := subsonic.New(artwork, listGenerator, playlists, mediaStreamer, archiver, players, externalInfo, dataStore) router := subsonic.New(artwork, playlists, mediaStreamer, archiver, players, externalInfo, dataStore)
return router return router
} }

View file

@ -1,4 +1,4 @@
package engine package core
import ( import (
"container/list" "container/list"
@ -17,22 +17,10 @@ type NowPlayingInfo struct {
} }
// This repo must have the semantics of a FIFO queue, for each playerId // This repo must have the semantics of a FIFO queue, for each playerId
type NowPlayingRepository interface { type NowPlaying interface {
// Insert at the head of the queue // Insert at the head of the queue
Enqueue(*NowPlayingInfo) error Enqueue(*NowPlayingInfo) error
// Removes and returns the element at the end of the queue
Dequeue(playerId int) (*NowPlayingInfo, error)
// Returns the element at the head of the queue (last inserted one)
Head(playerId int) (*NowPlayingInfo, error)
// Returns the element at the end of the queue (first inserted one)
Tail(playerId int) (*NowPlayingInfo, error)
// Size of the queue for the playerId
Count(playerId int) (int64, error)
// Returns all heads from all playerIds // Returns all heads from all playerIds
GetAll() ([]*NowPlayingInfo, error) GetAll() ([]*NowPlayingInfo, error)
} }
@ -41,55 +29,17 @@ var playerMap = sync.Map{}
type nowPlayingRepository struct{} type nowPlayingRepository struct{}
func NewNowPlayingRepository() NowPlayingRepository { func NewNowPlayingRepository() NowPlaying {
r := &nowPlayingRepository{} r := &nowPlayingRepository{}
return r return r
} }
func (r *nowPlayingRepository) getList(id int) *list.List {
l, _ := playerMap.LoadOrStore(id, list.New())
return l.(*list.List)
}
func (r *nowPlayingRepository) Enqueue(info *NowPlayingInfo) error { func (r *nowPlayingRepository) Enqueue(info *NowPlayingInfo) error {
l := r.getList(info.PlayerId) l := r.getList(info.PlayerId)
l.PushFront(info) l.PushFront(info)
return nil return nil
} }
func (r *nowPlayingRepository) Dequeue(playerId int) (*NowPlayingInfo, error) {
l := r.getList(playerId)
e := checkExpired(l, l.Back)
if e == nil {
return nil, nil
}
l.Remove(e)
return e.Value.(*NowPlayingInfo), nil
}
func (r *nowPlayingRepository) Head(playerId int) (*NowPlayingInfo, error) {
l := r.getList(playerId)
e := checkExpired(l, l.Front)
if e == nil {
return nil, nil
}
return e.Value.(*NowPlayingInfo), nil
}
func (r *nowPlayingRepository) Tail(playerId int) (*NowPlayingInfo, error) {
l := r.getList(playerId)
e := checkExpired(l, l.Back)
if e == nil {
return nil, nil
}
return e.Value.(*NowPlayingInfo), nil
}
func (r *nowPlayingRepository) Count(playerId int) (int64, error) {
l := r.getList(playerId)
return int64(l.Len()), nil
}
func (r *nowPlayingRepository) GetAll() ([]*NowPlayingInfo, error) { func (r *nowPlayingRepository) GetAll() ([]*NowPlayingInfo, error) {
var all []*NowPlayingInfo var all []*NowPlayingInfo
playerMap.Range(func(playerId, l interface{}) bool { playerMap.Range(func(playerId, l interface{}) bool {
@ -103,6 +53,44 @@ func (r *nowPlayingRepository) GetAll() ([]*NowPlayingInfo, error) {
return all, nil return all, nil
} }
func (r *nowPlayingRepository) getList(id int) *list.List {
l, _ := playerMap.LoadOrStore(id, list.New())
return l.(*list.List)
}
func (r *nowPlayingRepository) dequeue(playerId int) (*NowPlayingInfo, error) {
l := r.getList(playerId)
e := checkExpired(l, l.Back)
if e == nil {
return nil, nil
}
l.Remove(e)
return e.Value.(*NowPlayingInfo), nil
}
func (r *nowPlayingRepository) head(playerId int) (*NowPlayingInfo, error) {
l := r.getList(playerId)
e := checkExpired(l, l.Front)
if e == nil {
return nil, nil
}
return e.Value.(*NowPlayingInfo), nil
}
func (r *nowPlayingRepository) tail(playerId int) (*NowPlayingInfo, error) {
l := r.getList(playerId)
e := checkExpired(l, l.Back)
if e == nil {
return nil, nil
}
return e.Value.(*NowPlayingInfo), nil
}
func (r *nowPlayingRepository) count(playerId int) (int64, error) {
l := r.getList(playerId)
return int64(l.Len()), nil
}
func checkExpired(l *list.List, f func() *list.Element) *list.Element { func checkExpired(l *list.List, f func() *list.Element) *list.Element {
for { for {
e := f() e := f()

View file

@ -1,4 +1,4 @@
package engine package core
import ( import (
"sync" "sync"
@ -8,27 +8,27 @@ import (
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
) )
var _ = Describe("NowPlayingRepository", func() { var _ = Describe("NowPlaying", func() {
var repo NowPlayingRepository var repo *nowPlayingRepository
var now = time.Now() var now = time.Now()
var past = time.Time{} var past = time.Time{}
BeforeEach(func() { BeforeEach(func() {
playerMap = sync.Map{} playerMap = sync.Map{}
repo = NewNowPlayingRepository() repo = NewNowPlayingRepository().(*nowPlayingRepository)
}) })
It("enqueues and dequeues records", func() { It("enqueues and dequeues records", func() {
Expect(repo.Enqueue(&NowPlayingInfo{PlayerId: 1, TrackID: "AAA", Start: now})).To(BeNil()) Expect(repo.Enqueue(&NowPlayingInfo{PlayerId: 1, TrackID: "AAA", Start: now})).To(BeNil())
Expect(repo.Enqueue(&NowPlayingInfo{PlayerId: 1, TrackID: "BBB", Start: now})).To(BeNil()) Expect(repo.Enqueue(&NowPlayingInfo{PlayerId: 1, TrackID: "BBB", Start: now})).To(BeNil())
Expect(repo.Tail(1)).To(Equal(&NowPlayingInfo{PlayerId: 1, TrackID: "AAA", Start: now})) Expect(repo.tail(1)).To(Equal(&NowPlayingInfo{PlayerId: 1, TrackID: "AAA", Start: now}))
Expect(repo.Head(1)).To(Equal(&NowPlayingInfo{PlayerId: 1, TrackID: "BBB", Start: now})) Expect(repo.head(1)).To(Equal(&NowPlayingInfo{PlayerId: 1, TrackID: "BBB", Start: now}))
Expect(repo.Count(1)).To(Equal(int64(2))) Expect(repo.count(1)).To(Equal(int64(2)))
Expect(repo.Dequeue(1)).To(Equal(&NowPlayingInfo{PlayerId: 1, TrackID: "AAA", Start: now})) Expect(repo.dequeue(1)).To(Equal(&NowPlayingInfo{PlayerId: 1, TrackID: "AAA", Start: now}))
Expect(repo.Count(1)).To(Equal(int64(1))) Expect(repo.count(1)).To(Equal(int64(1)))
}) })
It("handles multiple players", func() { It("handles multiple players", func() {
@ -43,11 +43,11 @@ var _ = Describe("NowPlayingRepository", func() {
{PlayerId: 2, TrackID: "DDD", Start: now}, {PlayerId: 2, TrackID: "DDD", Start: now},
})) }))
Expect(repo.Count(2)).To(Equal(int64(2))) Expect(repo.count(2)).To(Equal(int64(2)))
Expect(repo.Count(2)).To(Equal(int64(2))) Expect(repo.count(2)).To(Equal(int64(2)))
Expect(repo.Tail(1)).To(Equal(&NowPlayingInfo{PlayerId: 1, TrackID: "AAA", Start: now})) Expect(repo.tail(1)).To(Equal(&NowPlayingInfo{PlayerId: 1, TrackID: "AAA", Start: now}))
Expect(repo.Head(2)).To(Equal(&NowPlayingInfo{PlayerId: 2, TrackID: "DDD", Start: now})) Expect(repo.head(2)).To(Equal(&NowPlayingInfo{PlayerId: 2, TrackID: "DDD", Start: now}))
}) })
It("handles expired items", func() { It("handles expired items", func() {

View file

@ -16,6 +16,7 @@ var Set = wire.NewSet(
NewTranscodingCache, NewTranscodingCache,
NewImageCache, NewImageCache,
NewArchiver, NewArchiver,
NewNowPlayingRepository,
NewExternalInfo, NewExternalInfo,
NewCacheWarmer, NewCacheWarmer,
LastFMNewClient, LastFMNewClient,

View file

@ -4,24 +4,25 @@ import (
"context" "context"
"errors" "errors"
"net/http" "net/http"
"time"
"github.com/deluan/navidrome/core"
"github.com/deluan/navidrome/log" "github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model" "github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/server/subsonic/engine"
"github.com/deluan/navidrome/server/subsonic/filter" "github.com/deluan/navidrome/server/subsonic/filter"
"github.com/deluan/navidrome/server/subsonic/responses" "github.com/deluan/navidrome/server/subsonic/responses"
"github.com/deluan/navidrome/utils" "github.com/deluan/navidrome/utils"
) )
type AlbumListController struct { type AlbumListController struct {
ds model.DataStore ds model.DataStore
listGen engine.ListGenerator nowPlaying core.NowPlaying
} }
func NewAlbumListController(ds model.DataStore, listGen engine.ListGenerator) *AlbumListController { func NewAlbumListController(ds model.DataStore, nowPlaying core.NowPlaying) *AlbumListController {
c := &AlbumListController{ c := &AlbumListController{
ds: ds, ds: ds,
listGen: listGen, nowPlaying: nowPlaying,
} }
return c return c
} }
@ -132,7 +133,8 @@ func (c *AlbumListController) GetStarred2(w http.ResponseWriter, r *http.Request
} }
func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) { func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
npInfos, err := c.listGen.GetNowPlaying(r.Context()) ctx := r.Context()
npInfo, err := c.nowPlaying.GetAll()
if err != nil { if err != nil {
log.Error(r, "Error retrieving now playing list", "error", err) log.Error(r, "Error retrieving now playing list", "error", err)
return nil, newError(responses.ErrorGeneric, "Internal Error") return nil, newError(responses.ErrorGeneric, "Internal Error")
@ -140,13 +142,18 @@ func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Reque
response := newResponse() response := newResponse()
response.NowPlaying = &responses.NowPlaying{} response.NowPlaying = &responses.NowPlaying{}
response.NowPlaying.Entry = make([]responses.NowPlayingEntry, len(npInfos)) response.NowPlaying.Entry = make([]responses.NowPlayingEntry, len(npInfo))
for i, entry := range npInfos { for i, np := range npInfo {
response.NowPlaying.Entry[i].Child = toChild(r.Context(), entry) mf, err := c.ds.MediaFile(ctx).Get(np.TrackID)
response.NowPlaying.Entry[i].UserName = entry.UserName if err != nil {
response.NowPlaying.Entry[i].MinutesAgo = entry.MinutesAgo return nil, newError(responses.ErrorGeneric, "Internal Error")
response.NowPlaying.Entry[i].PlayerId = entry.PlayerId }
response.NowPlaying.Entry[i].PlayerName = entry.PlayerName
response.NowPlaying.Entry[i].Child = childFromMediaFile(ctx, *mf)
response.NowPlaying.Entry[i].UserName = np.Username
response.NowPlaying.Entry[i].MinutesAgo = int(time.Since(np.Start).Minutes())
response.NowPlaying.Entry[i].PlayerId = np.PlayerId
response.NowPlaying.Entry[i].PlayerName = np.PlayerName
} }
return response, nil return response, nil
} }

View file

@ -23,22 +23,21 @@ const Version = "1.13.0"
type Handler = func(http.ResponseWriter, *http.Request) (*responses.Subsonic, error) type Handler = func(http.ResponseWriter, *http.Request) (*responses.Subsonic, error)
type Router struct { type Router struct {
Artwork core.Artwork Artwork core.Artwork
ListGenerator engine.ListGenerator Playlists engine.Playlists
Playlists engine.Playlists Streamer core.MediaStreamer
Streamer core.MediaStreamer Archiver core.Archiver
Archiver core.Archiver Players engine.Players
Players engine.Players ExternalInfo core.ExternalInfo
ExternalInfo core.ExternalInfo DataStore model.DataStore
DataStore model.DataStore
mux http.Handler mux http.Handler
} }
func New(artwork core.Artwork, listGenerator engine.ListGenerator, func New(artwork core.Artwork,
playlists engine.Playlists, streamer core.MediaStreamer, playlists engine.Playlists, streamer core.MediaStreamer,
archiver core.Archiver, players engine.Players, externalInfo core.ExternalInfo, ds model.DataStore) *Router { archiver core.Archiver, players engine.Players, externalInfo core.ExternalInfo, ds model.DataStore) *Router {
r := &Router{Artwork: artwork, ListGenerator: listGenerator, Playlists: playlists, r := &Router{Artwork: artwork, Playlists: playlists,
Streamer: streamer, Archiver: archiver, Players: players, ExternalInfo: externalInfo, DataStore: ds} Streamer: streamer, Archiver: archiver, Players: players, ExternalInfo: externalInfo, DataStore: ds}
r.mux = r.routes() r.mux = r.routes()
return r return r

View file

@ -1,41 +0,0 @@
package engine
import (
"context"
"time"
"github.com/deluan/navidrome/model"
)
type ListGenerator interface {
GetNowPlaying(ctx context.Context) (Entries, error)
}
func NewListGenerator(ds model.DataStore, npRepo NowPlayingRepository) ListGenerator {
return &listGenerator{ds, npRepo}
}
type listGenerator struct {
ds model.DataStore
npRepo NowPlayingRepository
}
func (g *listGenerator) GetNowPlaying(ctx context.Context) (Entries, error) {
npInfo, err := g.npRepo.GetAll()
if err != nil {
return nil, err
}
entries := make(Entries, len(npInfo))
for i, np := range npInfo {
mf, err := g.ds.MediaFile(ctx).Get(np.TrackID)
if err != nil {
return nil, err
}
entries[i] = FromMediaFile(mf)
entries[i].UserName = np.Username
entries[i].MinutesAgo = int(time.Since(np.Start).Minutes())
entries[i].PlayerId = np.PlayerId
entries[i].PlayerName = np.PlayerName
}
return entries, nil
}

View file

@ -5,8 +5,6 @@ import (
) )
var Set = wire.NewSet( var Set = wire.NewSet(
NewListGenerator,
NewPlaylists, NewPlaylists,
NewNowPlayingRepository,
NewPlayers, NewPlayers,
) )

View file

@ -6,20 +6,20 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/deluan/navidrome/core"
"github.com/deluan/navidrome/log" "github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model" "github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/model/request" "github.com/deluan/navidrome/model/request"
"github.com/deluan/navidrome/server/subsonic/engine"
"github.com/deluan/navidrome/server/subsonic/responses" "github.com/deluan/navidrome/server/subsonic/responses"
"github.com/deluan/navidrome/utils" "github.com/deluan/navidrome/utils"
) )
type MediaAnnotationController struct { type MediaAnnotationController struct {
ds model.DataStore ds model.DataStore
npRepo engine.NowPlayingRepository npRepo core.NowPlaying
} }
func NewMediaAnnotationController(ds model.DataStore, npr engine.NowPlayingRepository) *MediaAnnotationController { func NewMediaAnnotationController(ds model.DataStore, npr core.NowPlaying) *MediaAnnotationController {
return &MediaAnnotationController{ds: ds, npRepo: npr} return &MediaAnnotationController{ds: ds, npRepo: npr}
} }
@ -176,7 +176,7 @@ func (c *MediaAnnotationController) scrobblerNowPlaying(ctx context.Context, pla
log.Info("Now Playing", "title", mf.Title, "artist", mf.Artist, "user", username) log.Info("Now Playing", "title", mf.Title, "artist", mf.Artist, "user", username)
info := &engine.NowPlayingInfo{TrackID: trackId, Username: username, Start: time.Now(), PlayerId: playerId, PlayerName: playerName} info := &core.NowPlayingInfo{TrackID: trackId, Username: username, Start: time.Now(), PlayerId: playerId, PlayerName: playerName}
return mf, c.npRepo.Enqueue(info) return mf, c.npRepo.Enqueue(info)
} }

View file

@ -6,7 +6,7 @@
package subsonic package subsonic
import ( import (
"github.com/deluan/navidrome/server/subsonic/engine" "github.com/deluan/navidrome/core"
"github.com/google/wire" "github.com/google/wire"
) )
@ -26,15 +26,15 @@ func initBrowsingController(router *Router) *BrowsingController {
func initAlbumListController(router *Router) *AlbumListController { func initAlbumListController(router *Router) *AlbumListController {
dataStore := router.DataStore dataStore := router.DataStore
listGenerator := router.ListGenerator nowPlaying := core.NewNowPlayingRepository()
albumListController := NewAlbumListController(dataStore, listGenerator) albumListController := NewAlbumListController(dataStore, nowPlaying)
return albumListController return albumListController
} }
func initMediaAnnotationController(router *Router) *MediaAnnotationController { func initMediaAnnotationController(router *Router) *MediaAnnotationController {
dataStore := router.DataStore dataStore := router.DataStore
nowPlayingRepository := engine.NewNowPlayingRepository() nowPlaying := core.NewNowPlayingRepository()
mediaAnnotationController := NewMediaAnnotationController(dataStore, nowPlayingRepository) mediaAnnotationController := NewMediaAnnotationController(dataStore, nowPlaying)
return mediaAnnotationController return mediaAnnotationController
} }
@ -87,5 +87,5 @@ var allProviders = wire.NewSet(
NewUsersController, NewUsersController,
NewMediaRetrievalController, NewMediaRetrievalController,
NewStreamController, NewStreamController,
NewBookmarksController, engine.NewNowPlayingRepository, wire.FieldsOf(new(*Router), "Artwork", "ListGenerator", "Playlists", "Streamer", "Archiver", "DataStore", "ExternalInfo"), NewBookmarksController, core.NewNowPlayingRepository, wire.FieldsOf(new(*Router), "Artwork", "Playlists", "Streamer", "Archiver", "DataStore", "ExternalInfo"),
) )

View file

@ -3,7 +3,7 @@
package subsonic package subsonic
import ( import (
"github.com/deluan/navidrome/server/subsonic/engine" "github.com/deluan/navidrome/core"
"github.com/google/wire" "github.com/google/wire"
) )
@ -18,8 +18,8 @@ var allProviders = wire.NewSet(
NewMediaRetrievalController, NewMediaRetrievalController,
NewStreamController, NewStreamController,
NewBookmarksController, NewBookmarksController,
engine.NewNowPlayingRepository, core.NewNowPlayingRepository,
wire.FieldsOf(new(*Router), "Artwork", "ListGenerator", "Playlists", "Streamer", "Archiver", "DataStore", "ExternalInfo"), wire.FieldsOf(new(*Router), "Artwork", "Playlists", "Streamer", "Archiver", "DataStore", "ExternalInfo"),
) )
func initSystemController(router *Router) *SystemController { func initSystemController(router *Router) *SystemController {