Rename 'Cover' to the more generic term 'Artwork'

This commit is contained in:
Deluan 2020-07-31 09:31:19 -04:00
parent cefdeee495
commit 2b59d4b87a
9 changed files with 81 additions and 81 deletions

View File

@ -41,8 +41,8 @@ func CreateAppRouter() *app.Router {
func CreateSubsonicAPIRouter() (*subsonic.Router, error) { func CreateSubsonicAPIRouter() (*subsonic.Router, error) {
dataStore := persistence.New() dataStore := persistence.New()
browser := engine.NewBrowser(dataStore) browser := engine.NewBrowser(dataStore)
coverCache := core.NewImageCache() artworkCache := core.NewImageCache()
cover := core.NewCover(dataStore, coverCache) artwork := core.NewArtwork(dataStore, artworkCache)
nowPlayingRepository := engine.NewNowPlayingRepository() nowPlayingRepository := engine.NewNowPlayingRepository()
listGenerator := engine.NewListGenerator(dataStore, nowPlayingRepository) listGenerator := engine.NewListGenerator(dataStore, nowPlayingRepository)
users := engine.NewUsers(dataStore) users := engine.NewUsers(dataStore)
@ -54,7 +54,7 @@ func CreateSubsonicAPIRouter() (*subsonic.Router, error) {
transcodingCache := core.NewTranscodingCache() transcodingCache := core.NewTranscodingCache()
mediaStreamer := core.NewMediaStreamer(dataStore, transcoderTranscoder, transcodingCache) mediaStreamer := core.NewMediaStreamer(dataStore, transcoderTranscoder, transcodingCache)
players := engine.NewPlayers(dataStore) players := engine.NewPlayers(dataStore)
router := subsonic.New(browser, cover, listGenerator, users, playlists, ratings, scrobbler, search, mediaStreamer, players) router := subsonic.New(browser, artwork, listGenerator, users, playlists, ratings, scrobbler, search, mediaStreamer, players)
return router, nil return router, nil
} }

View File

@ -24,39 +24,39 @@ import (
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
) )
type Cover interface { type Artwork interface {
Get(ctx context.Context, id string, size int, out io.Writer) error Get(ctx context.Context, id string, size int, out io.Writer) error
} }
type CoverCache FileCache type ArtworkCache FileCache
func NewCover(ds model.DataStore, cache CoverCache) Cover { func NewArtwork(ds model.DataStore, cache ArtworkCache) Artwork {
return &cover{ds: ds, cache: cache} return &artwork{ds: ds, cache: cache}
} }
type cover struct { type artwork struct {
ds model.DataStore ds model.DataStore
cache FileCache cache FileCache
} }
type coverInfo struct { type imageInfo struct {
c *cover c *artwork
path string path string
size int size int
lastUpdate time.Time lastUpdate time.Time
} }
func (ci *coverInfo) String() string { func (ci *imageInfo) String() string {
return fmt.Sprintf("%s.%d.%s.%d", ci.path, ci.size, ci.lastUpdate.Format(time.RFC3339Nano), conf.Server.CoverJpegQuality) return fmt.Sprintf("%s.%d.%s.%d", ci.path, ci.size, ci.lastUpdate.Format(time.RFC3339Nano), conf.Server.CoverJpegQuality)
} }
func (c *cover) Get(ctx context.Context, id string, size int, out io.Writer) error { func (c *artwork) Get(ctx context.Context, id string, size int, out io.Writer) error {
path, lastUpdate, err := c.getCoverPath(ctx, id) path, lastUpdate, err := c.getImagePath(ctx, id)
if err != nil && err != model.ErrNotFound { if err != nil && err != model.ErrNotFound {
return err return err
} }
info := &coverInfo{ info := &imageInfo{
c: c, c: c,
path: path, path: path,
size: size, size: size,
@ -73,7 +73,7 @@ func (c *cover) Get(ctx context.Context, id string, size int, out io.Writer) err
return err return err
} }
func (c *cover) getCoverPath(ctx context.Context, id string) (path string, lastUpdated time.Time, err error) { func (c *artwork) getImagePath(ctx context.Context, id string) (path string, lastUpdated time.Time, err error) {
// If id is an album cover ID // If id is an album cover ID
if strings.HasPrefix(id, "al-") { if strings.HasPrefix(id, "al-") {
log.Trace(ctx, "Looking for album art", "id", id) log.Trace(ctx, "Looking for album art", "id", id)
@ -102,10 +102,10 @@ func (c *cover) getCoverPath(ctx context.Context, id string) (path string, lastU
// if the mediafile does not have a coverArt, fallback to the album cover // if the mediafile does not have a coverArt, fallback to the album cover
log.Trace(ctx, "Media file does not contain art. Falling back to album art", "id", id, "albumId", "al-"+mf.AlbumID) log.Trace(ctx, "Media file does not contain art. Falling back to album art", "id", id, "albumId", "al-"+mf.AlbumID)
return c.getCoverPath(ctx, "al-"+mf.AlbumID) return c.getImagePath(ctx, "al-"+mf.AlbumID)
} }
func (c *cover) getCover(ctx context.Context, path string, size int) (reader io.Reader, err error) { func (c *artwork) getArtwork(ctx context.Context, path string, size int) (reader io.Reader, err error) {
defer func() { defer func() {
if err != nil { if err != nil {
log.Warn(ctx, "Error extracting image", "path", path, "size", size, err) log.Warn(ctx, "Error extracting image", "path", path, "size", size, err)
@ -114,7 +114,7 @@ func (c *cover) getCover(ctx context.Context, path string, size int) (reader io.
}() }()
if path == "" { if path == "" {
return nil, errors.New("empty path given for cover") return nil, errors.New("empty path given for artwork")
} }
var data []byte var data []byte
@ -184,13 +184,13 @@ func readFromFile(path string) ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
func NewImageCache() CoverCache { func NewImageCache() ArtworkCache {
return NewFileCache("Image", conf.Server.ImageCacheSize, consts.ImageCacheDir, consts.DefaultImageCacheMaxItems, return NewFileCache("Image", conf.Server.ImageCacheSize, consts.ImageCacheDir, consts.DefaultImageCacheMaxItems,
func(ctx context.Context, arg fmt.Stringer) (io.Reader, error) { func(ctx context.Context, arg fmt.Stringer) (io.Reader, error) {
info := arg.(*coverInfo) info := arg.(*imageInfo)
reader, err := info.c.getCover(ctx, info.path, info.size) reader, err := info.c.getArtwork(ctx, info.path, info.size)
if err != nil { if err != nil {
log.Error(ctx, "Error loading cover art", "path", info.path, "size", info.size, err) log.Error(ctx, "Error loading artwork art", "path", info.path, "size", info.size, err)
return nil, err return nil, err
} }
return reader, nil return reader, nil

View File

@ -15,8 +15,8 @@ import (
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
) )
var _ = Describe("Cover", func() { var _ = Describe("Artwork", func() {
var cover Cover var artwork Artwork
var ds model.DataStore var ds model.DataStore
ctx := log.NewContext(context.TODO()) ctx := log.NewContext(context.TODO())
@ -32,57 +32,57 @@ var _ = Describe("Cover", func() {
conf.Server.ImageCacheSize = "100MB" conf.Server.ImageCacheSize = "100MB"
cache := NewImageCache() cache := NewImageCache()
Eventually(func() bool { return cache.Ready() }).Should(BeTrue()) Eventually(func() bool { return cache.Ready() }).Should(BeTrue())
cover = NewCover(ds, cache) artwork = NewArtwork(ds, cache)
}) })
AfterEach(func() { AfterEach(func() {
os.RemoveAll(conf.Server.DataFolder) os.RemoveAll(conf.Server.DataFolder)
}) })
It("retrieves the external cover art for an album", func() { It("retrieves the external artwork art for an album", func() {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
Expect(cover.Get(ctx, "al-444", 0, buf)).To(BeNil()) Expect(artwork.Get(ctx, "al-444", 0, buf)).To(BeNil())
_, format, err := image.Decode(bytes.NewReader(buf.Bytes())) _, format, err := image.Decode(bytes.NewReader(buf.Bytes()))
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(format).To(Equal("jpeg")) Expect(format).To(Equal("jpeg"))
}) })
It("retrieves the embedded cover art for an album", func() { It("retrieves the embedded artwork art for an album", func() {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
Expect(cover.Get(ctx, "al-222", 0, buf)).To(BeNil()) Expect(artwork.Get(ctx, "al-222", 0, buf)).To(BeNil())
_, format, err := image.Decode(bytes.NewReader(buf.Bytes())) _, format, err := image.Decode(bytes.NewReader(buf.Bytes()))
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(format).To(Equal("jpeg")) Expect(format).To(Equal("jpeg"))
}) })
It("returns the default cover if album does not have cover", func() { It("returns the default artwork if album does not have artwork", func() {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
Expect(cover.Get(ctx, "al-333", 0, buf)).To(BeNil()) Expect(artwork.Get(ctx, "al-333", 0, buf)).To(BeNil())
_, format, err := image.Decode(bytes.NewReader(buf.Bytes())) _, format, err := image.Decode(bytes.NewReader(buf.Bytes()))
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(format).To(Equal("png")) Expect(format).To(Equal("png"))
}) })
It("returns the default cover if album is not found", func() { It("returns the default artwork if album is not found", func() {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
Expect(cover.Get(ctx, "al-0101", 0, buf)).To(BeNil()) Expect(artwork.Get(ctx, "al-0101", 0, buf)).To(BeNil())
_, format, err := image.Decode(bytes.NewReader(buf.Bytes())) _, format, err := image.Decode(bytes.NewReader(buf.Bytes()))
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(format).To(Equal("png")) Expect(format).To(Equal("png"))
}) })
It("retrieves the original cover art from a media_file", func() { It("retrieves the original artwork art from a media_file", func() {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
Expect(cover.Get(ctx, "123", 0, buf)).To(BeNil()) Expect(artwork.Get(ctx, "123", 0, buf)).To(BeNil())
img, format, err := image.Decode(bytes.NewReader(buf.Bytes())) img, format, err := image.Decode(bytes.NewReader(buf.Bytes()))
Expect(err).To(BeNil()) Expect(err).To(BeNil())
@ -91,20 +91,20 @@ var _ = Describe("Cover", func() {
Expect(img.Bounds().Size().Y).To(Equal(600)) Expect(img.Bounds().Size().Y).To(Equal(600))
}) })
It("retrieves the album cover art if media_file does not have one", func() { It("retrieves the album artwork art if media_file does not have one", func() {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
Expect(cover.Get(ctx, "456", 0, buf)).To(BeNil()) Expect(artwork.Get(ctx, "456", 0, buf)).To(BeNil())
_, format, err := image.Decode(bytes.NewReader(buf.Bytes())) _, format, err := image.Decode(bytes.NewReader(buf.Bytes()))
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(format).To(Equal("jpeg")) Expect(format).To(Equal("jpeg"))
}) })
It("resized cover art as requested", func() { It("resized artwork art as requested", func() {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
Expect(cover.Get(ctx, "123", 200, buf)).To(BeNil()) Expect(artwork.Get(ctx, "123", 200, buf)).To(BeNil())
img, format, err := image.Decode(bytes.NewReader(buf.Bytes())) img, format, err := image.Decode(bytes.NewReader(buf.Bytes()))
Expect(err).To(BeNil()) Expect(err).To(BeNil())
@ -118,14 +118,14 @@ var _ = Describe("Cover", func() {
ds.Album(ctx).(*persistence.MockAlbum).SetError(true) ds.Album(ctx).(*persistence.MockAlbum).SetError(true)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
Expect(cover.Get(ctx, "al-222", 0, buf)).To(MatchError("Error!")) Expect(artwork.Get(ctx, "al-222", 0, buf)).To(MatchError("Error!"))
}) })
It("returns err if gets error from media_file table", func() { It("returns err if gets error from media_file table", func() {
ds.MediaFile(ctx).(*persistence.MockMediaFile).SetError(true) ds.MediaFile(ctx).(*persistence.MockMediaFile).SetError(true)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
Expect(cover.Get(ctx, "123", 0, buf)).To(MatchError("Error!")) Expect(artwork.Get(ctx, "123", 0, buf)).To(MatchError("Error!"))
}) })
}) })
}) })

View File

@ -6,7 +6,7 @@ import (
) )
var Set = wire.NewSet( var Set = wire.NewSet(
NewCover, NewArtwork,
NewMediaStreamer, NewMediaStreamer,
NewTranscodingCache, NewTranscodingCache,
NewImageCache, NewImageCache,

View File

@ -23,7 +23,7 @@ type Handler = func(http.ResponseWriter, *http.Request) (*responses.Subsonic, er
type Router struct { type Router struct {
Browser engine.Browser Browser engine.Browser
Cover core.Cover Artwork core.Artwork
ListGenerator engine.ListGenerator ListGenerator engine.ListGenerator
Playlists engine.Playlists Playlists engine.Playlists
Ratings engine.Ratings Ratings engine.Ratings
@ -36,10 +36,10 @@ type Router struct {
mux http.Handler mux http.Handler
} }
func New(browser engine.Browser, cover core.Cover, listGenerator engine.ListGenerator, users engine.Users, func New(browser engine.Browser, artwork core.Artwork, listGenerator engine.ListGenerator, users engine.Users,
playlists engine.Playlists, ratings engine.Ratings, scrobbler engine.Scrobbler, search engine.Search, playlists engine.Playlists, ratings engine.Ratings, scrobbler engine.Scrobbler, search engine.Search,
streamer core.MediaStreamer, players engine.Players) *Router { streamer core.MediaStreamer, players engine.Players) *Router {
r := &Router{Browser: browser, Cover: cover, ListGenerator: listGenerator, Playlists: playlists, r := &Router{Browser: browser, Artwork: artwork, ListGenerator: listGenerator, Playlists: playlists,
Ratings: ratings, Scrobbler: scrobbler, Search: search, Users: users, Streamer: streamer, Players: players} Ratings: ratings, Scrobbler: scrobbler, Search: search, Users: users, Streamer: streamer, Players: players}
r.mux = r.routes() r.mux = r.routes()
return r return r

View File

@ -14,11 +14,11 @@ import (
) )
type MediaRetrievalController struct { type MediaRetrievalController struct {
cover core.Cover artwork core.Artwork
} }
func NewMediaRetrievalController(cover core.Cover) *MediaRetrievalController { func NewMediaRetrievalController(artwork core.Artwork) *MediaRetrievalController {
return &MediaRetrievalController{cover: cover} return &MediaRetrievalController{artwork: artwork}
} }
func (c *MediaRetrievalController) GetAvatar(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) { func (c *MediaRetrievalController) GetAvatar(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
@ -41,12 +41,12 @@ func (c *MediaRetrievalController) GetCoverArt(w http.ResponseWriter, r *http.Re
size := utils.ParamInt(r, "size", 0) size := utils.ParamInt(r, "size", 0)
w.Header().Set("cache-control", "public, max-age=315360000") w.Header().Set("cache-control", "public, max-age=315360000")
err = c.cover.Get(r.Context(), id, size, w) err = c.artwork.Get(r.Context(), id, size, w)
switch { switch {
case err == model.ErrNotFound: case err == model.ErrNotFound:
log.Error(r, "Couldn't find coverArt", "id", id, err) log.Error(r, "Couldn't find coverArt", "id", id, err)
return nil, NewError(responses.ErrorDataNotFound, "Cover not found") return nil, NewError(responses.ErrorDataNotFound, "Artwork not found")
case err != nil: case err != nil:
log.Error(r, "Error retrieving coverArt", "id", id, err) log.Error(r, "Error retrieving coverArt", "id", id, err)
return nil, NewError(responses.ErrorGeneric, "Internal Error") return nil, NewError(responses.ErrorGeneric, "Internal Error")

View File

@ -11,44 +11,27 @@ import (
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
) )
type fakeCover struct {
data string
err error
recvId string
recvSize int
}
func (c *fakeCover) Get(ctx context.Context, id string, size int, out io.Writer) error {
if c.err != nil {
return c.err
}
c.recvId = id
c.recvSize = size
_, err := out.Write([]byte(c.data))
return err
}
var _ = Describe("MediaRetrievalController", func() { var _ = Describe("MediaRetrievalController", func() {
var controller *MediaRetrievalController var controller *MediaRetrievalController
var cover *fakeCover var artwork *fakeArtwork
var w *httptest.ResponseRecorder var w *httptest.ResponseRecorder
BeforeEach(func() { BeforeEach(func() {
cover = &fakeCover{} artwork = &fakeArtwork{}
controller = NewMediaRetrievalController(cover) controller = NewMediaRetrievalController(artwork)
w = httptest.NewRecorder() w = httptest.NewRecorder()
}) })
Describe("GetCoverArt", func() { Describe("GetCoverArt", func() {
It("should return data for that id", func() { It("should return data for that id", func() {
cover.data = "image data" artwork.data = "image data"
r := newGetRequest("id=34", "size=128") r := newGetRequest("id=34", "size=128")
_, err := controller.GetCoverArt(w, r) _, err := controller.GetCoverArt(w, r)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(cover.recvId).To(Equal("34")) Expect(artwork.recvId).To(Equal("34"))
Expect(cover.recvSize).To(Equal(128)) Expect(artwork.recvSize).To(Equal(128))
Expect(w.Body.String()).To(Equal(cover.data)) Expect(w.Body.String()).To(Equal(artwork.data))
}) })
It("should fail if missing id parameter", func() { It("should fail if missing id parameter", func() {
@ -59,15 +42,15 @@ var _ = Describe("MediaRetrievalController", func() {
}) })
It("should fail when the file is not found", func() { It("should fail when the file is not found", func() {
cover.err = model.ErrNotFound artwork.err = model.ErrNotFound
r := newGetRequest("id=34", "size=128") r := newGetRequest("id=34", "size=128")
_, err := controller.GetCoverArt(w, r) _, err := controller.GetCoverArt(w, r)
Expect(err).To(MatchError("Cover not found")) Expect(err).To(MatchError("Artwork not found"))
}) })
It("should fail when there is an unknown error", func() { It("should fail when there is an unknown error", func() {
cover.err = errors.New("weird error") artwork.err = errors.New("weird error")
r := newGetRequest("id=34", "size=128") r := newGetRequest("id=34", "size=128")
_, err := controller.GetCoverArt(w, r) _, err := controller.GetCoverArt(w, r)
@ -75,3 +58,20 @@ var _ = Describe("MediaRetrievalController", func() {
}) })
}) })
}) })
type fakeArtwork struct {
data string
err error
recvId string
recvSize int
}
func (c *fakeArtwork) Get(ctx context.Context, id string, size int, out io.Writer) error {
if c.err != nil {
return c.err
}
c.recvId = id
c.recvSize = size
_, err := out.Write([]byte(c.data))
return err
}

View File

@ -53,8 +53,8 @@ func initUsersController(router *Router) *UsersController {
} }
func initMediaRetrievalController(router *Router) *MediaRetrievalController { func initMediaRetrievalController(router *Router) *MediaRetrievalController {
cover := router.Cover artwork := router.Artwork
mediaRetrievalController := NewMediaRetrievalController(cover) mediaRetrievalController := NewMediaRetrievalController(artwork)
return mediaRetrievalController return mediaRetrievalController
} }
@ -75,5 +75,5 @@ var allProviders = wire.NewSet(
NewSearchingController, NewSearchingController,
NewUsersController, NewUsersController,
NewMediaRetrievalController, NewMediaRetrievalController,
NewStreamController, wire.FieldsOf(new(*Router), "Browser", "Cover", "ListGenerator", "Playlists", "Ratings", "Scrobbler", "Search", "Streamer"), NewStreamController, wire.FieldsOf(new(*Router), "Browser", "Artwork", "ListGenerator", "Playlists", "Ratings", "Scrobbler", "Search", "Streamer"),
) )

View File

@ -16,7 +16,7 @@ var allProviders = wire.NewSet(
NewUsersController, NewUsersController,
NewMediaRetrievalController, NewMediaRetrievalController,
NewStreamController, NewStreamController,
wire.FieldsOf(new(*Router), "Browser", "Cover", "ListGenerator", "Playlists", "Ratings", "Scrobbler", "Search", "Streamer"), wire.FieldsOf(new(*Router), "Browser", "Artwork", "ListGenerator", "Playlists", "Ratings", "Scrobbler", "Search", "Streamer"),
) )
func initSystemController(router *Router) *SystemController { func initSystemController(router *Router) *SystemController {