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,10 +4,11 @@ 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"
@ -15,13 +16,13 @@ import (
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

@ -24,7 +24,6 @@ type Handler = func(http.ResponseWriter, *http.Request) (*responses.Subsonic, er
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
@ -35,10 +34,10 @@ type Router struct {
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 {