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()
artworkCache := core.NewImageCache()
artwork := core.NewArtwork(dataStore, artworkCache)
nowPlayingRepository := engine.NewNowPlayingRepository()
listGenerator := engine.NewListGenerator(dataStore, nowPlayingRepository)
playlists := engine.NewPlaylists(dataStore)
transcoderTranscoder := transcoder.New()
transcodingCache := core.NewTranscodingCache()
@ -55,7 +53,7 @@ func CreateSubsonicAPIRouter() *subsonic.Router {
client := core.LastFMNewClient()
spotifyClient := core.SpotifyNewClient()
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
}

View file

@ -1,4 +1,4 @@
package engine
package core
import (
"container/list"
@ -17,22 +17,10 @@ type NowPlayingInfo struct {
}
// 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
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
GetAll() ([]*NowPlayingInfo, error)
}
@ -41,55 +29,17 @@ var playerMap = sync.Map{}
type nowPlayingRepository struct{}
func NewNowPlayingRepository() NowPlayingRepository {
func NewNowPlayingRepository() NowPlaying {
r := &nowPlayingRepository{}
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 {
l := r.getList(info.PlayerId)
l.PushFront(info)
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) {
var all []*NowPlayingInfo
playerMap.Range(func(playerId, l interface{}) bool {
@ -103,6 +53,44 @@ func (r *nowPlayingRepository) GetAll() ([]*NowPlayingInfo, error) {
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 {
for {
e := f()

View file

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

View file

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

View file

@ -4,10 +4,11 @@ import (
"context"
"errors"
"net/http"
"time"
"github.com/deluan/navidrome/core"
"github.com/deluan/navidrome/log"
"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/responses"
"github.com/deluan/navidrome/utils"
@ -15,13 +16,13 @@ import (
type AlbumListController struct {
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{
ds: ds,
listGen: listGen,
nowPlaying: nowPlaying,
}
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) {
npInfos, err := c.listGen.GetNowPlaying(r.Context())
ctx := r.Context()
npInfo, err := c.nowPlaying.GetAll()
if err != nil {
log.Error(r, "Error retrieving now playing list", "error", err)
return nil, newError(responses.ErrorGeneric, "Internal Error")
@ -140,13 +142,18 @@ func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Reque
response := newResponse()
response.NowPlaying = &responses.NowPlaying{}
response.NowPlaying.Entry = make([]responses.NowPlayingEntry, len(npInfos))
for i, entry := range npInfos {
response.NowPlaying.Entry[i].Child = toChild(r.Context(), entry)
response.NowPlaying.Entry[i].UserName = entry.UserName
response.NowPlaying.Entry[i].MinutesAgo = entry.MinutesAgo
response.NowPlaying.Entry[i].PlayerId = entry.PlayerId
response.NowPlaying.Entry[i].PlayerName = entry.PlayerName
response.NowPlaying.Entry = make([]responses.NowPlayingEntry, len(npInfo))
for i, np := range npInfo {
mf, err := c.ds.MediaFile(ctx).Get(np.TrackID)
if err != nil {
return nil, newError(responses.ErrorGeneric, "Internal Error")
}
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
}

View file

@ -24,7 +24,6 @@ type Handler = func(http.ResponseWriter, *http.Request) (*responses.Subsonic, er
type Router struct {
Artwork core.Artwork
ListGenerator engine.ListGenerator
Playlists engine.Playlists
Streamer core.MediaStreamer
Archiver core.Archiver
@ -35,10 +34,10 @@ type Router struct {
mux http.Handler
}
func New(artwork core.Artwork, listGenerator engine.ListGenerator,
func New(artwork core.Artwork,
playlists engine.Playlists, streamer core.MediaStreamer,
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}
r.mux = r.routes()
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(
NewListGenerator,
NewPlaylists,
NewNowPlayingRepository,
NewPlayers,
)

View file

@ -6,20 +6,20 @@ import (
"net/http"
"time"
"github.com/deluan/navidrome/core"
"github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/model/request"
"github.com/deluan/navidrome/server/subsonic/engine"
"github.com/deluan/navidrome/server/subsonic/responses"
"github.com/deluan/navidrome/utils"
)
type MediaAnnotationController struct {
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}
}
@ -176,7 +176,7 @@ func (c *MediaAnnotationController) scrobblerNowPlaying(ctx context.Context, pla
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)
}

View file

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