From c430401ea9fc65c2cb5da1133cf3487f71a48073 Mon Sep 17 00:00:00 2001 From: Deluan Date: Mon, 19 Dec 2022 13:59:24 -0500 Subject: [PATCH] Remove current artwork implementation --- core/artwork.go | 177 +------------------------- core/artwork_test.go | 6 +- model/album.go | 8 +- model/artwork_id.go | 61 +++++++++ model/artwork_id_test.go | 34 +++++ model/mediafile.go | 67 ++-------- model/mediafile_internal_test.go | 58 --------- model/mediafile_test.go | 35 ++++- persistence/persistence_suite_test.go | 6 +- server/subsonic/browsing.go | 4 +- server/subsonic/helpers.go | 12 +- 11 files changed, 164 insertions(+), 304 deletions(-) create mode 100644 model/artwork_id.go create mode 100644 model/artwork_id_test.go diff --git a/core/artwork.go b/core/artwork.go index c353f728..a46b720e 100644 --- a/core/artwork.go +++ b/core/artwork.go @@ -1,29 +1,18 @@ package core import ( - "bytes" "context" - "errors" "fmt" - "image" _ "image/gif" - "image/jpeg" - "image/png" _ "image/png" "io" - "os" - "strings" "sync" "time" - "github.com/dhowden/tag" - "github.com/disintegration/imaging" "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/consts" - "github.com/navidrome/navidrome/log" "github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/resources" - "github.com/navidrome/navidrome/utils" "github.com/navidrome/navidrome/utils/cache" _ "golang.org/x/image/webp" ) @@ -43,6 +32,10 @@ type artwork struct { cache cache.FileCache } +func (a *artwork) Get(ctx context.Context, id string, size int) (io.ReadCloser, error) { + return resources.FS().Open(consts.PlaceholderAlbumArt) +} + type imageInfo struct { a *artwork id string @@ -55,160 +48,6 @@ func (ci *imageInfo) Key() string { return fmt.Sprintf("%s.%d.%s.%d", ci.path, ci.size, ci.lastUpdate.Format(time.RFC3339Nano), conf.Server.CoverJpegQuality) } -func (a *artwork) Get(ctx context.Context, id string, size int) (io.ReadCloser, error) { - path, lastUpdate, err := a.getImagePath(ctx, id) - if err != nil && !errors.Is(err, model.ErrNotFound) { - return nil, err - } - - if !conf.Server.DevFastAccessCoverArt { - if stat, err := os.Stat(path); err == nil { - lastUpdate = stat.ModTime() - } - } - - info := &imageInfo{ - a: a, - id: id, - path: path, - size: size, - lastUpdate: lastUpdate, - } - - r, err := a.cache.Get(ctx, info) - if err != nil { - log.Error(ctx, "Error accessing image cache", "path", path, "size", size, err) - return nil, err - } - return r, err -} - -func (a *artwork) getImagePath(ctx context.Context, id string) (path string, lastUpdated time.Time, err error) { - // If id is an album cover ID - if strings.HasPrefix(id, "al-") { - log.Trace(ctx, "Looking for album art", "id", id) - id = strings.TrimPrefix(id, "al-") - var al *model.Album - al, err = a.ds.Album(ctx).Get(id) - if err != nil { - return - } - if al.CoverArtId == "" { - err = model.ErrNotFound - } - return al.CoverArtPath, al.UpdatedAt, err - } - - log.Trace(ctx, "Looking for media file art", "id", id) - - // Check if id is a mediaFile id - var mf *model.MediaFile - mf, err = a.ds.MediaFile(ctx).Get(id) - - // If it is not, may be an albumId - if errors.Is(err, model.ErrNotFound) { - return a.getImagePath(ctx, "al-"+id) - } - if err != nil { - return - } - - // If it is a mediaFile, and it has cover art, return it (if feature is disabled, skip) - if !conf.Server.DevFastAccessCoverArt && mf.HasCoverArt { - return mf.Path, mf.UpdatedAt, nil - } - - // 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) - return a.getImagePath(ctx, "al-"+mf.AlbumID) -} - -func (a *artwork) getArtwork(ctx context.Context, id string, path string, size int) (reader io.ReadCloser, err error) { - defer func() { - if err != nil { - log.Warn(ctx, "Error extracting image", "path", path, "size", size, err) - reader, err = resources.FS().Open(consts.PlaceholderAlbumArt) - - if size != 0 && err == nil { - var r io.ReadCloser - r, err = resources.FS().Open(consts.PlaceholderAlbumArt) - reader, err = resizeImage(r, size, true) - } - } - }() - - if path == "" { - return nil, errors.New("empty path given for artwork") - } - - if size == 0 { - // If requested original size, just read from the file - if utils.IsAudioFile(path) { - reader, err = readFromTag(path) - } else { - reader, err = readFromFile(path) - } - } else { - // If requested a resized image, get the original (possibly from cache) and resize it - var r io.ReadCloser - r, err = a.Get(ctx, id, 0) - if err != nil { - return - } - defer r.Close() - reader, err = resizeImage(r, size, false) - } - - return reader, err -} - -func resizeImage(reader io.Reader, size int, usePng bool) (io.ReadCloser, error) { - img, _, err := image.Decode(reader) - if err != nil { - return nil, err - } - - // Preserve the aspect ratio of the image. - var m *image.NRGBA - bounds := img.Bounds() - if bounds.Max.X > bounds.Max.Y { - m = imaging.Resize(img, size, 0, imaging.Lanczos) - } else { - m = imaging.Resize(img, 0, size, imaging.Lanczos) - } - - buf := new(bytes.Buffer) - if usePng { - err = png.Encode(buf, m) - } else { - err = jpeg.Encode(buf, m, &jpeg.Options{Quality: conf.Server.CoverJpegQuality}) - } - return io.NopCloser(buf), err -} - -func readFromTag(path string) (io.ReadCloser, error) { - f, err := os.Open(path) - if err != nil { - return nil, err - } - defer f.Close() - - m, err := tag.ReadFrom(f) - if err != nil { - return nil, err - } - - picture := m.Picture() - if picture == nil { - return nil, errors.New("file does not contain embedded art") - } - return io.NopCloser(bytes.NewReader(picture.Data)), nil -} - -func readFromFile(path string) (io.ReadCloser, error) { - return os.Open(path) -} - var ( onceImageCache sync.Once instanceImageCache ArtworkCache @@ -218,13 +57,7 @@ func GetImageCache() ArtworkCache { onceImageCache.Do(func() { instanceImageCache = cache.NewFileCache("Image", conf.Server.ImageCacheSize, consts.ImageCacheDir, consts.DefaultImageCacheMaxItems, func(ctx context.Context, arg cache.Item) (io.Reader, error) { - info := arg.(*imageInfo) - reader, err := info.a.getArtwork(ctx, info.id, info.path, info.size) - if err != nil { - log.Error(ctx, "Error loading artwork art", "path", info.path, "size", info.size, err) - return nil, err - } - return reader, nil + return nil, nil }) }) return instanceImageCache diff --git a/core/artwork_test.go b/core/artwork_test.go index 609b4ce3..5c95455e 100644 --- a/core/artwork_test.go +++ b/core/artwork_test.go @@ -21,9 +21,9 @@ var _ = Describe("Artwork", func() { BeforeEach(func() { ds = &tests.MockDataStore{MockedTranscoding: &tests.MockTranscodingRepo{}} ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{ - {ID: "222", CoverArtId: "123", CoverArtPath: "tests/fixtures/test.mp3"}, - {ID: "333", CoverArtId: ""}, - {ID: "444", CoverArtId: "444", CoverArtPath: "tests/fixtures/cover.jpg"}, + {ID: "222", EmbedArtId: "123", EmbedArtPath: "tests/fixtures/test.mp3"}, + {ID: "333", EmbedArtId: ""}, + {ID: "444", EmbedArtId: "444", EmbedArtPath: "tests/fixtures/cover.jpg"}, }) ds.MediaFile(ctx).(*tests.MockMediaFileRepo).SetData(model.MediaFiles{ {ID: "123", AlbumID: "222", Path: "tests/fixtures/test.mp3", HasCoverArt: true}, diff --git a/model/album.go b/model/album.go index 7d6a965e..9e7203ae 100644 --- a/model/album.go +++ b/model/album.go @@ -7,8 +7,8 @@ type Album struct { ID string `structs:"id" json:"id" orm:"column(id)"` Name string `structs:"name" json:"name"` - CoverArtPath string `structs:"cover_art_path" json:"coverArtPath"` - CoverArtId string `structs:"cover_art_id" json:"coverArtId"` + EmbedArtPath string `structs:"cover_art_path" json:"coverArtPath"` + EmbedArtId string `structs:"cover_art_id" json:"coverArtId"` ArtistID string `structs:"artist_id" json:"artistId" orm:"column(artist_id)"` Artist string `structs:"artist" json:"artist"` AlbumArtistID string `structs:"album_artist_id" json:"albumArtistId" orm:"column(album_artist_id)"` @@ -39,6 +39,10 @@ type Album struct { UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"` } +func (a Album) CoverArtID() ArtworkID { + return artworkIDFromAlbum(a) +} + type ( Albums []Album DiscID struct { diff --git a/model/artwork_id.go b/model/artwork_id.go new file mode 100644 index 00000000..83a0855c --- /dev/null +++ b/model/artwork_id.go @@ -0,0 +1,61 @@ +package model + +import ( + "errors" + "fmt" + "strconv" + "strings" + "time" +) + +type Kind struct{ prefix string } + +var ( + KindMediaFileArtwork = Kind{"mf"} + KindAlbumArtwork = Kind{"al"} +) + +type ArtworkID struct { + Kind Kind + ID string + LastAccess time.Time +} + +func (id ArtworkID) String() string { + return fmt.Sprintf("%s-%s-%x", id.Kind.prefix, id.ID, id.LastAccess.Unix()) +} + +func ParseArtworkID(id string) (ArtworkID, error) { + parts := strings.Split(id, "-") + if len(parts) != 3 { + return ArtworkID{}, errors.New("invalid artwork id") + } + lastUpdate, err := strconv.ParseInt(parts[2], 16, 64) + if err != nil { + return ArtworkID{}, err + } + if parts[0] != KindAlbumArtwork.prefix && parts[0] != KindMediaFileArtwork.prefix { + return ArtworkID{}, errors.New("invalid artwork kind") + } + return ArtworkID{ + Kind: Kind{parts[0]}, + ID: parts[1], + LastAccess: time.Unix(lastUpdate, 0), + }, nil +} + +func artworkIDFromAlbum(al Album) ArtworkID { + return ArtworkID{ + Kind: KindAlbumArtwork, + ID: al.ID, + LastAccess: al.UpdatedAt, + } +} + +func artworkIDFromMediaFile(mf MediaFile) ArtworkID { + return ArtworkID{ + Kind: KindMediaFileArtwork, + ID: mf.ID, + LastAccess: mf.UpdatedAt, + } +} diff --git a/model/artwork_id_test.go b/model/artwork_id_test.go new file mode 100644 index 00000000..c6d8645b --- /dev/null +++ b/model/artwork_id_test.go @@ -0,0 +1,34 @@ +package model_test + +import ( + "time" + + "github.com/navidrome/navidrome/model" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("ParseArtworkID()", func() { + It("parses album artwork ids", func() { + id, err := model.ParseArtworkID("al-1234-ff") + Expect(err).ToNot(HaveOccurred()) + Expect(id.Kind).To(Equal(model.KindAlbumArtwork)) + Expect(id.ID).To(Equal("1234")) + Expect(id.LastAccess).To(Equal(time.Unix(255, 0))) + }) + It("parses media file artwork ids", func() { + id, err := model.ParseArtworkID("mf-a6f8d2b1-ffff") + Expect(err).ToNot(HaveOccurred()) + Expect(id.Kind).To(Equal(model.KindMediaFileArtwork)) + Expect(id.ID).To(Equal("a6f8d2b1")) + Expect(id.LastAccess).To(Equal(time.Unix(65535, 0))) + }) + It("fails to parse malformed ids", func() { + _, err := model.ParseArtworkID("a6f8d2b1") + Expect(err).To(MatchError("invalid artwork id")) + }) + It("fails to parse ids with invalid kind", func() { + _, err := model.ParseArtworkID("xx-a6f8d2b1-ff") + Expect(err).To(MatchError("invalid artwork kind")) + }) +}) diff --git a/model/mediafile.go b/model/mediafile.go index 3331972a..e0ce424b 100644 --- a/model/mediafile.go +++ b/model/mediafile.go @@ -2,7 +2,6 @@ package model import ( "mime" - "os" "path/filepath" "strings" "time" @@ -65,10 +64,19 @@ type MediaFile struct { UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"` // Time of file last update (mtime) } -func (mf *MediaFile) ContentType() string { +func (mf MediaFile) ContentType() string { return mime.TypeByExtension("." + mf.Suffix) } +func (mf MediaFile) CoverArtID() ArtworkID { + // If it is a mediaFile, and it has cover art, return it (if feature is disabled, skip) + if mf.HasCoverArt && !conf.Server.DevFastAccessCoverArt { + return artworkIDFromMediaFile(mf) + } + // if the mediaFile does not have a coverArt, fallback to the album cover + return artworkIDFromAlbum(Album{ID: mf.AlbumID, UpdatedAt: mf.UpdatedAt}) +} + type MediaFiles []MediaFile func (mfs MediaFiles) Dirs() []string { @@ -88,7 +96,6 @@ func (mfs MediaFiles) ToAlbum() Album { var songArtistIds []string var mbzAlbumIds []string var comments []string - var firstPath string for _, m := range mfs { // We assume these attributes are all the same for all songs on an album a.ID = m.AlbumID @@ -128,12 +135,9 @@ func (mfs MediaFiles) ToAlbum() Album { m.Album, m.AlbumArtist, m.Artist, m.SortAlbumName, m.SortAlbumArtistName, m.SortArtistName, m.DiscSubtitle) - if m.HasCoverArt { - a.CoverArtId = m.ID - a.CoverArtPath = m.Path - } - if firstPath == "" { - firstPath = m.Path + if m.HasCoverArt && a.EmbedArtId == "" { + a.EmbedArtId = m.ID + a.EmbedArtPath = m.Path } } comments = slices.Compact(comments) @@ -150,13 +154,6 @@ func (mfs MediaFiles) ToAlbum() Album { a.AllArtistIDs = strings.Join(slices.Compact(songArtistIds), " ") a.MbzAlbumID = slice.MostFrequent(mbzAlbumIds) - if a.CoverArtPath == "" || !strings.HasPrefix(conf.Server.CoverArtPriority, "embedded") { - if path := getCoverFromPath(firstPath, a.CoverArtPath); path != "" { - a.CoverArtId = "al-" + a.ID - a.CoverArtPath = path - } - } - return a } @@ -194,44 +191,6 @@ func fixAlbumArtist(a Album, albumArtistIds []string) Album { return a } -// GetCoverFromPath accepts a path to a file, and returns a path to an eligible cover image from the -// file's directory (as configured with CoverArtPriority). If no cover file is found, among -// available choices, or an error occurs, an empty string is returned. If HasEmbeddedCover is true, -// and 'embedded' is matched among eligible choices, GetCoverFromPath will return early with an -// empty path. -// TODO: Move to scanner (or at least out of here) -func getCoverFromPath(mediaPath string, embeddedPath string) string { - n, err := os.Open(filepath.Dir(mediaPath)) - if err != nil { - return "" - } - - defer n.Close() - names, err := n.Readdirnames(-1) - if err != nil { - return "" - } - - for _, p := range strings.Split(conf.Server.CoverArtPriority, ",") { - pat := strings.ToLower(strings.TrimSpace(p)) - if pat == "embedded" { - if embeddedPath != "" { - return "" - } - continue - } - - for _, name := range names { - match, _ := filepath.Match(pat, strings.ToLower(name)) - if match && utils.IsImageFile(name) { - return filepath.Join(filepath.Dir(mediaPath), name) - } - } - } - - return "" -} - type MediaFileRepository interface { CountAll(options ...QueryOptions) (int64, error) Exists(id string) (bool, error) diff --git a/model/mediafile_internal_test.go b/model/mediafile_internal_test.go index b86427f6..2f902f8e 100644 --- a/model/mediafile_internal_test.go +++ b/model/mediafile_internal_test.go @@ -1,10 +1,6 @@ package model import ( - "os" - "path/filepath" - - "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/consts" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -55,57 +51,3 @@ var _ = Describe("fixAlbumArtist", func() { }) }) }) - -var _ = Describe("getCoverFromPath", func() { - var testFolder, testPath, embeddedPath string - BeforeEach(func() { - testFolder, _ = os.MkdirTemp("", "album_persistence_tests") - if err := os.MkdirAll(testFolder, 0777); err != nil { - panic(err) - } - if _, err := os.Create(filepath.Join(testFolder, "Cover.jpeg")); err != nil { - panic(err) - } - if _, err := os.Create(filepath.Join(testFolder, "FRONT.PNG")); err != nil { - panic(err) - } - testPath = filepath.Join(testFolder, "somefile.test") - embeddedPath = filepath.Join(testFolder, "somefile.mp3") - }) - AfterEach(func() { - _ = os.RemoveAll(testFolder) - }) - - It("returns audio file for embedded cover", func() { - conf.Server.CoverArtPriority = "embedded, cover.*, front.*" - Expect(getCoverFromPath(testPath, embeddedPath)).To(Equal("")) - }) - - It("returns external file when no embedded cover exists", func() { - conf.Server.CoverArtPriority = "embedded, cover.*, front.*" - Expect(getCoverFromPath(testPath, "")).To(Equal(filepath.Join(testFolder, "Cover.jpeg"))) - }) - - It("returns embedded cover even if not first choice", func() { - conf.Server.CoverArtPriority = "something.png, embedded, cover.*, front.*" - Expect(getCoverFromPath(testPath, embeddedPath)).To(Equal("")) - }) - - It("returns first correct match case-insensitively", func() { - conf.Server.CoverArtPriority = "embedded, cover.jpg, front.svg, front.png" - Expect(getCoverFromPath(testPath, "")).To(Equal(filepath.Join(testFolder, "FRONT.PNG"))) - }) - - It("returns match for embedded pattern", func() { - conf.Server.CoverArtPriority = "embedded, cover.jp?g, front.png" - Expect(getCoverFromPath(testPath, "")).To(Equal(filepath.Join(testFolder, "Cover.jpeg"))) - }) - - It("returns empty string if no match was found", func() { - conf.Server.CoverArtPriority = "embedded, cover.jpg, front.apng" - Expect(getCoverFromPath(testPath, "")).To(Equal("")) - }) - - // Reset configuration to default. - conf.Server.CoverArtPriority = "embedded, cover.*, front.*" -}) diff --git a/model/mediafile_test.go b/model/mediafile_test.go index b816b082..eefd0a6c 100644 --- a/model/mediafile_test.go +++ b/model/mediafile_test.go @@ -3,6 +3,8 @@ package model_test import ( "time" + "github.com/navidrome/navidrome/conf" + "github.com/navidrome/navidrome/conf/configtest" . "github.com/navidrome/navidrome/model" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -26,7 +28,7 @@ var _ = Describe("MediaFiles", func() { SortAlbumName: "SortAlbumName", SortArtistName: "SortArtistName", SortAlbumArtistName: "SortAlbumArtistName", OrderAlbumName: "OrderAlbumName", OrderArtistName: "OrderArtistName", OrderAlbumArtistName: "OrderAlbumArtistName", MbzAlbumArtistID: "MbzAlbumArtistID", MbzAlbumType: "MbzAlbumType", MbzAlbumComment: "MbzAlbumComment", - Compilation: true, CatalogNum: "CatalogNum", HasCoverArt: true, + Compilation: true, CatalogNum: "CatalogNum", HasCoverArt: true, Path: "/music/file.mp3", }, } }) @@ -49,7 +51,8 @@ var _ = Describe("MediaFiles", func() { Expect(album.MbzAlbumComment).To(Equal("MbzAlbumComment")) Expect(album.CatalogNum).To(Equal("CatalogNum")) Expect(album.Compilation).To(BeTrue()) - Expect(album.CoverArtId).To(Equal("2")) + Expect(album.EmbedArtId).To(Equal("2")) + Expect(album.EmbedArtPath).To(Equal("/music/file.mp3")) }) }) Context("Aggregated attributes", func() { @@ -220,6 +223,34 @@ var _ = Describe("MediaFiles", func() { }) }) +var _ = Describe("MediaFile", func() { + BeforeEach(func() { + DeferCleanup(configtest.SetupConfig()) + conf.Server.DevFastAccessCoverArt = false + }) + Describe(".CoverArtId()", func() { + It("returns its own id if it HasCoverArt", func() { + mf := MediaFile{ID: "111", AlbumID: "1", HasCoverArt: true} + id := mf.CoverArtID() + Expect(id.Kind).To(Equal(KindMediaFileArtwork)) + Expect(id.ID).To(Equal(mf.ID)) + }) + It("returns its album id if HasCoverArt is false", func() { + mf := MediaFile{ID: "111", AlbumID: "1", HasCoverArt: false} + id := mf.CoverArtID() + Expect(id.Kind).To(Equal(KindAlbumArtwork)) + Expect(id.ID).To(Equal(mf.AlbumID)) + }) + It("returns its album id if DevFastAccessCoverArt is enabled", func() { + conf.Server.DevFastAccessCoverArt = true + mf := MediaFile{ID: "111", AlbumID: "1", HasCoverArt: true} + id := mf.CoverArtID() + Expect(id.Kind).To(Equal(KindAlbumArtwork)) + Expect(id.ID).To(Equal(mf.AlbumID)) + }) + }) +}) + func t(v string) time.Time { var timeFormats = []string{"2006-01-02", "2006-01-02 15:04", "2006-01-02 15:04:05", "2006-01-02T15:04:05", "2006-01-02T15:04", "2006-01-02 15:04:05.999999999 -0700 MST"} for _, f := range timeFormats { diff --git a/persistence/persistence_suite_test.go b/persistence/persistence_suite_test.go index dd169375..9ec2f85e 100644 --- a/persistence/persistence_suite_test.go +++ b/persistence/persistence_suite_test.go @@ -46,9 +46,9 @@ var ( ) var ( - albumSgtPeppers = model.Album{ID: "101", Name: "Sgt Peppers", Artist: "The Beatles", OrderAlbumName: "sgt peppers", AlbumArtistID: "3", Genre: "Rock", Genres: model.Genres{genreRock}, CoverArtId: "1", CoverArtPath: P("/beatles/1/sgt/a day.mp3"), SongCount: 1, MaxYear: 1967, FullText: " beatles peppers sgt the"} - albumAbbeyRoad = model.Album{ID: "102", Name: "Abbey Road", Artist: "The Beatles", OrderAlbumName: "abbey road", AlbumArtistID: "3", Genre: "Rock", Genres: model.Genres{genreRock}, CoverArtId: "2", CoverArtPath: P("/beatles/1/come together.mp3"), SongCount: 1, MaxYear: 1969, FullText: " abbey beatles road the"} - albumRadioactivity = model.Album{ID: "103", Name: "Radioactivity", Artist: "Kraftwerk", OrderAlbumName: "radioactivity", AlbumArtistID: "2", Genre: "Electronic", Genres: model.Genres{genreElectronic, genreRock}, CoverArtId: "3", CoverArtPath: P("/kraft/radio/radio.mp3"), SongCount: 2, FullText: " kraftwerk radioactivity"} + albumSgtPeppers = model.Album{ID: "101", Name: "Sgt Peppers", Artist: "The Beatles", OrderAlbumName: "sgt peppers", AlbumArtistID: "3", Genre: "Rock", Genres: model.Genres{genreRock}, EmbedArtId: "1", EmbedArtPath: P("/beatles/1/sgt/a day.mp3"), SongCount: 1, MaxYear: 1967, FullText: " beatles peppers sgt the"} + albumAbbeyRoad = model.Album{ID: "102", Name: "Abbey Road", Artist: "The Beatles", OrderAlbumName: "abbey road", AlbumArtistID: "3", Genre: "Rock", Genres: model.Genres{genreRock}, EmbedArtId: "2", EmbedArtPath: P("/beatles/1/come together.mp3"), SongCount: 1, MaxYear: 1969, FullText: " abbey beatles road the"} + albumRadioactivity = model.Album{ID: "103", Name: "Radioactivity", Artist: "Kraftwerk", OrderAlbumName: "radioactivity", AlbumArtistID: "2", Genre: "Electronic", Genres: model.Genres{genreElectronic, genreRock}, EmbedArtId: "3", EmbedArtPath: P("/kraft/radio/radio.mp3"), SongCount: 2, FullText: " kraftwerk radioactivity"} testAlbums = model.Albums{ albumSgtPeppers, albumAbbeyRoad, diff --git a/server/subsonic/browsing.go b/server/subsonic/browsing.go index c6ecc447..3531e4db 100644 --- a/server/subsonic/browsing.go +++ b/server/subsonic/browsing.go @@ -360,7 +360,7 @@ func (api *Router) buildAlbumDirectory(ctx context.Context, album *model.Album) } dir.UserRating = album.Rating dir.SongCount = album.SongCount - dir.CoverArt = album.CoverArtId + dir.CoverArt = album.CoverArtID().String() if album.Starred { dir.Starred = &album.StarredAt } @@ -380,7 +380,7 @@ func (api *Router) buildAlbum(ctx context.Context, album *model.Album, mfs model dir.Name = album.Name dir.Artist = album.AlbumArtist dir.ArtistId = album.AlbumArtistID - dir.CoverArt = album.CoverArtId + dir.CoverArt = album.CoverArtID().String() dir.SongCount = album.SongCount dir.Duration = int(album.Duration) dir.PlayCount = album.PlayCount diff --git a/server/subsonic/helpers.go b/server/subsonic/helpers.go index 24cf2602..c0f55327 100644 --- a/server/subsonic/helpers.go +++ b/server/subsonic/helpers.go @@ -80,7 +80,7 @@ func toArtists(ctx context.Context, artists model.Artists) []responses.Artist { return as } -func toArtist(ctx context.Context, a model.Artist) responses.Artist { +func toArtist(_ context.Context, a model.Artist) responses.Artist { artist := responses.Artist{ Id: a.ID, Name: a.Name, @@ -147,11 +147,7 @@ func childFromMediaFile(ctx context.Context, mf model.MediaFile) responses.Child child.Size = mf.Size child.Suffix = mf.Suffix child.BitRate = mf.BitRate - if mf.HasCoverArt { - child.CoverArt = mf.ID - } else { - child.CoverArt = "al-" + mf.AlbumID - } + child.CoverArt = mf.CoverArtID().String() child.ContentType = mf.ContentType() player, ok := request.PlayerFrom(ctx) if ok && player.ReportRealPath { @@ -202,7 +198,7 @@ func childrenFromMediaFiles(ctx context.Context, mfs model.MediaFiles) []respons return children } -func childFromAlbum(ctx context.Context, al model.Album) responses.Child { +func childFromAlbum(_ context.Context, al model.Album) responses.Child { child := responses.Child{} child.Id = al.ID child.IsDir = true @@ -212,7 +208,7 @@ func childFromAlbum(ctx context.Context, al model.Album) responses.Child { child.Artist = al.AlbumArtist child.Year = al.MaxYear child.Genre = al.Genre - child.CoverArt = al.CoverArtId + child.CoverArt = al.CoverArtID().String() child.Created = &al.CreatedAt child.Parent = al.AlbumArtistID child.ArtistId = al.AlbumArtistID