Move cover art discovery (temporarily) to model

This commit is contained in:
Deluan 2022-12-19 11:13:39 -05:00 committed by Deluan Quintão
parent 28e7371d93
commit 83ff44f5f4
2 changed files with 112 additions and 1 deletions

View File

@ -2,9 +2,12 @@ package model
import (
"mime"
"os"
"path/filepath"
"strings"
"time"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/utils"
"github.com/navidrome/navidrome/utils/number"
@ -75,6 +78,7 @@ 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
@ -115,8 +119,11 @@ func (mfs MediaFiles) ToAlbum() Album {
m.SortAlbumName, m.SortAlbumArtistName, m.SortArtistName,
m.DiscSubtitle)
if m.HasCoverArt {
// TODO CoverArtPriority
a.CoverArtId = m.ID
a.CoverArtPath = m.Path
}
if firstPath == "" {
firstPath = m.Path
}
}
comments = slices.Compact(comments)
@ -132,6 +139,14 @@ func (mfs MediaFiles) ToAlbum() Album {
slices.Sort(songArtistIds)
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
}
@ -169,6 +184,44 @@ 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)

View File

@ -1,6 +1,10 @@
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"
@ -51,3 +55,57 @@ 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.*"
})