Move alternative tag names mapping to metadata

This commit is contained in:
Deluan 2022-12-18 23:18:27 -05:00 committed by Deluan Quintão
parent 0c22af3585
commit 6f5aaa1ec4
7 changed files with 146 additions and 120 deletions

View File

@ -39,6 +39,13 @@ func (e *Extractor) Parse(files ...string) (map[string]metadata.ParsedTags, erro
return fileTags, nil
}
func (e *Extractor) CustomMappings() metadata.ParsedTags {
return metadata.ParsedTags{
"disc": {"tpa"},
"has_picture": {"metadata_block_picture"},
}
}
func (e *Extractor) extractMetadata(filePath, info string) (metadata.ParsedTags, error) {
tags := e.parseInfo(info)
if len(tags) == 0 {
@ -46,17 +53,6 @@ func (e *Extractor) extractMetadata(filePath, info string) (metadata.ParsedTags,
return nil, errors.New("not a media file")
}
alternativeTags := map[string][]string{
"disc": {"tpa"},
"has_picture": {"metadata_block_picture"},
}
for tagName, alternatives := range alternativeTags {
for _, altName := range alternatives {
if altValue, ok := tags[altName]; ok {
tags[tagName] = append(tags[tagName], altValue...)
}
}
}
return tags, nil
}
@ -186,17 +182,18 @@ func (e *Extractor) parseDuration(tag string) string {
}
func (e *Extractor) parseChannels(tag string) string {
if tag == "mono" {
switch tag {
case "mono":
return "1"
} else if tag == "stereo" {
case "stereo":
return "2"
} else if tag == "5.1" {
case "5.1":
return "6"
} else if tag == "7.1" {
case "7.1":
return "8"
default:
return "0"
}
return "0"
}
// Inputs will always be absolute paths

View File

@ -77,6 +77,8 @@ Input #0, ogg, from '/Users/deluan/Music/iTunes/iTunes Media/Music/_Testes/Jamai
metadata_block_picture: AAAAAwAAAAppbWFnZS9qcGVnAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4Id/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQ
TITLE : Jamaican In New York (Album Version)`
md, _ := e.extractMetadata("tests/fixtures/test.mp3", output)
Expect(md).To(HaveKey("metadata_block_picture"))
md = md.Map(e.CustomMappings())
Expect(md).To(HaveKey("has_picture"))
})

View File

@ -16,10 +16,9 @@ import (
"github.com/navidrome/navidrome/log"
)
type ParsedTags = map[string][]string
type Extractor interface {
Parse(files ...string) (map[string]ParsedTags, error)
CustomMappings() ParsedTags
}
var extractors = map[string]Extractor{}
@ -49,6 +48,7 @@ func Extract(files ...string) (map[string]Tags, error) {
continue
}
tags = tags.Map(p.CustomMappings())
result[filePath] = Tags{
filePath: filePath,
fileInfo: fileInfo,
@ -59,10 +59,27 @@ func Extract(files ...string) (map[string]Tags, error) {
return result, nil
}
type ParsedTags map[string][]string
func (p ParsedTags) Map(customMappings ParsedTags) ParsedTags {
if customMappings == nil {
return p
}
for tagName, alternatives := range customMappings {
for _, altName := range alternatives {
if altValue, ok := p[altName]; ok {
p[tagName] = append(p[tagName], altValue...)
delete(p, altName)
}
}
}
return p
}
type Tags struct {
filePath string
fileInfo os.FileInfo
tags map[string][]string
tags ParsedTags
}
// Common tags
@ -92,6 +109,7 @@ func (t Tags) Bpm() int { return (int)(math.Round(t.getFloat("tbpm", "
func (t Tags) HasPicture() bool { return t.getFirstTagValue("has_picture") != "" }
// MusicBrainz Identifiers
func (t Tags) MbzReleaseTrackID() string {
return t.getMbzID("musicbrainz_releasetrackid", "musicbrainz release track id")
}
@ -148,10 +166,10 @@ func (t Tags) getAllTagValues(tagNames ...string) []string {
return values
}
func (t Tags) getSortTag(originalTag string, tagNamess ...string) string {
func (t Tags) getSortTag(originalTag string, tagNames ...string) string {
formats := []string{"sort%s", "sort_%s", "sort-%s", "%ssort", "%s_sort", "%s-sort"}
all := []string{originalTag}
for _, tag := range tagNamess {
for _, tag := range tagNames {
for _, format := range formats {
name := fmt.Sprintf(format, tag)
all = append(all, name)

View File

@ -0,0 +1,89 @@
package metadata
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Tags", func() {
Describe("getYear", func() {
It("parses the year correctly", func() {
var examples = map[string]int{
"1985": 1985,
"2002-01": 2002,
"1969.06": 1969,
"1980.07.25": 1980,
"2004-00-00": 2004,
"2013-May-12": 2013,
"May 12, 2016": 2016,
"01/10/1990": 1990,
}
for tag, expected := range examples {
md := &Tags{}
md.tags = map[string][]string{"date": {tag}}
Expect(md.Year()).To(Equal(expected))
}
})
It("returns 0 if year is invalid", func() {
md := &Tags{}
md.tags = map[string][]string{"date": {"invalid"}}
Expect(md.Year()).To(Equal(0))
})
})
Describe("getMbzID", func() {
It("return a valid MBID", func() {
md := &Tags{}
md.tags = map[string][]string{
"musicbrainz_trackid": {"8f84da07-09a0-477b-b216-cc982dabcde1"},
"musicbrainz_releasetrackid": {"6caf16d3-0b20-3fe6-8020-52e31831bc11"},
"musicbrainz_albumid": {"f68c985d-f18b-4f4a-b7f0-87837cf3fbf9"},
"musicbrainz_artistid": {"89ad4ac3-39f7-470e-963a-56509c546377"},
"musicbrainz_albumartistid": {"ada7a83c-e3e1-40f1-93f9-3e73dbc9298a"},
}
Expect(md.MbzTrackID()).To(Equal("8f84da07-09a0-477b-b216-cc982dabcde1"))
Expect(md.MbzReleaseTrackID()).To(Equal("6caf16d3-0b20-3fe6-8020-52e31831bc11"))
Expect(md.MbzAlbumID()).To(Equal("f68c985d-f18b-4f4a-b7f0-87837cf3fbf9"))
Expect(md.MbzArtistID()).To(Equal("89ad4ac3-39f7-470e-963a-56509c546377"))
Expect(md.MbzAlbumArtistID()).To(Equal("ada7a83c-e3e1-40f1-93f9-3e73dbc9298a"))
})
It("return empty string for invalid MBID", func() {
md := &Tags{}
md.tags = map[string][]string{
"musicbrainz_trackid": {"11406732-6"},
"musicbrainz_albumid": {"11406732"},
"musicbrainz_artistid": {"200455"},
"musicbrainz_albumartistid": {"194"},
}
Expect(md.MbzTrackID()).To(Equal(""))
Expect(md.MbzAlbumID()).To(Equal(""))
Expect(md.MbzArtistID()).To(Equal(""))
Expect(md.MbzAlbumArtistID()).To(Equal(""))
})
})
Describe("getAllTagValues", func() {
It("returns values from all tag names", func() {
md := &Tags{}
md.tags = map[string][]string{
"genre": {"Rock", "Pop", "New Wave"},
}
Expect(md.Genres()).To(ConsistOf("Rock", "Pop", "New Wave"))
})
})
Describe("Bpm", func() {
var t *Tags
BeforeEach(func() {
t = &Tags{tags: map[string][]string{
"fbpm": []string{"141.7"},
}}
})
It("rounds a floating point fBPM tag", func() {
Expect(t.Bpm()).To(Equal(142))
})
})
})

View File

@ -1,7 +1,10 @@
package metadata
package metadata_test
import (
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/scanner/metadata"
_ "github.com/navidrome/navidrome/scanner/metadata/ffmpeg"
_ "github.com/navidrome/navidrome/scanner/metadata/taglib"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@ -13,7 +16,7 @@ var _ = Describe("Tags", func() {
})
It("correctly parses metadata from all files in folder", func() {
mds, err := Extract("tests/fixtures/test.mp3", "tests/fixtures/test.ogg")
mds, err := metadata.Extract("tests/fixtures/test.mp3", "tests/fixtures/test.ogg")
Expect(err).NotTo(HaveOccurred())
Expect(mds).To(HaveLen(2))
@ -52,85 +55,4 @@ var _ = Describe("Tags", func() {
Expect(m.BitRate()).To(BeElementOf(18, 39))
})
})
Describe("getYear", func() {
It("parses the year correctly", func() {
var examples = map[string]int{
"1985": 1985,
"2002-01": 2002,
"1969.06": 1969,
"1980.07.25": 1980,
"2004-00-00": 2004,
"2013-May-12": 2013,
"May 12, 2016": 2016,
"01/10/1990": 1990,
}
for tag, expected := range examples {
md := &Tags{}
md.tags = map[string][]string{"date": {tag}}
Expect(md.Year()).To(Equal(expected))
}
})
It("returns 0 if year is invalid", func() {
md := &Tags{}
md.tags = map[string][]string{"date": {"invalid"}}
Expect(md.Year()).To(Equal(0))
})
})
Describe("getMbzID", func() {
It("return a valid MBID", func() {
md := &Tags{}
md.tags = map[string][]string{
"musicbrainz_trackid": {"8f84da07-09a0-477b-b216-cc982dabcde1"},
"musicbrainz_releasetrackid": {"6caf16d3-0b20-3fe6-8020-52e31831bc11"},
"musicbrainz_albumid": {"f68c985d-f18b-4f4a-b7f0-87837cf3fbf9"},
"musicbrainz_artistid": {"89ad4ac3-39f7-470e-963a-56509c546377"},
"musicbrainz_albumartistid": {"ada7a83c-e3e1-40f1-93f9-3e73dbc9298a"},
}
Expect(md.MbzTrackID()).To(Equal("8f84da07-09a0-477b-b216-cc982dabcde1"))
Expect(md.MbzReleaseTrackID()).To(Equal("6caf16d3-0b20-3fe6-8020-52e31831bc11"))
Expect(md.MbzAlbumID()).To(Equal("f68c985d-f18b-4f4a-b7f0-87837cf3fbf9"))
Expect(md.MbzArtistID()).To(Equal("89ad4ac3-39f7-470e-963a-56509c546377"))
Expect(md.MbzAlbumArtistID()).To(Equal("ada7a83c-e3e1-40f1-93f9-3e73dbc9298a"))
})
It("return empty string for invalid MBID", func() {
md := &Tags{}
md.tags = map[string][]string{
"musicbrainz_trackid": {"11406732-6"},
"musicbrainz_albumid": {"11406732"},
"musicbrainz_artistid": {"200455"},
"musicbrainz_albumartistid": {"194"},
}
Expect(md.MbzTrackID()).To(Equal(""))
Expect(md.MbzAlbumID()).To(Equal(""))
Expect(md.MbzArtistID()).To(Equal(""))
Expect(md.MbzAlbumArtistID()).To(Equal(""))
})
})
Describe("getAllTagValues", func() {
It("returns values from all tag names", func() {
md := &Tags{}
md.tags = map[string][]string{
"genre": {"Rock", "Pop", "New Wave"},
}
Expect(md.Genres()).To(ConsistOf("Rock", "Pop", "New Wave"))
})
})
Describe("Bpm", func() {
var t *Tags
BeforeEach(func() {
t = &Tags{tags: map[string][]string{
"fbpm": []string{"141.7"},
}}
})
It("rounds a floating point fBPM tag", func() {
Expect(t.Bpm()).To(Equal(142))
})
})
})

View File

@ -24,6 +24,15 @@ func (e *Extractor) Parse(paths ...string) (map[string]metadata.ParsedTags, erro
return fileTags, nil
}
func (e *Extractor) CustomMappings() metadata.ParsedTags {
return metadata.ParsedTags{
"title": {"titlesort"},
"album": {"albumsort"},
"artist": {"artistsort"},
"tracknumber": {"trck", "_track"},
}
}
func (e *Extractor) extractMetadata(filePath string) (metadata.ParsedTags, error) {
tags, err := Read(filePath)
if err != nil {
@ -31,13 +40,6 @@ func (e *Extractor) extractMetadata(filePath string) (metadata.ParsedTags, error
return nil, err
}
alternativeTags := map[string][]string{
"title": {"titlesort"},
"album": {"albumsort"},
"artist": {"artistsort"},
"tracknumber": {"trck", "_track"},
}
if length, ok := tags["lengthinmilliseconds"]; ok && len(length) > 0 {
millis, _ := strconv.Atoi(length[0])
if duration := float64(millis) / 1000.0; duration > 0 {
@ -45,13 +47,6 @@ func (e *Extractor) extractMetadata(filePath string) (metadata.ParsedTags, error
}
}
for tagName, alternatives := range alternativeTags {
for _, altName := range alternatives {
if altValue, ok := tags[altName]; ok {
tags[tagName] = append(tags[tagName], altValue...)
}
}
}
return tags, nil
}

View File

@ -42,7 +42,6 @@ var _ = Describe("Extractor", func() {
Expect(m).To(HaveKeyWithValue("tcmp", []string{"1"})) // Compilation
Expect(m).To(HaveKeyWithValue("genre", []string{"Rock"}))
Expect(m).To(HaveKeyWithValue("date", []string{"2014", "2014"}))
Expect(m).To(HaveKeyWithValue("tracknumber", []string{"2/10", "2/10", "2"}))
Expect(m).To(HaveKeyWithValue("discnumber", []string{"1/2"}))
Expect(m).To(HaveKeyWithValue("has_picture", []string{"true"}))
Expect(m).To(HaveKeyWithValue("duration", []string{"1.02"}))
@ -52,6 +51,10 @@ var _ = Describe("Extractor", func() {
Expect(m).To(HaveKeyWithValue("lyrics", []string{"Lyrics 1\rLyrics 2"}))
Expect(m).To(HaveKeyWithValue("bpm", []string{"123"}))
Expect(m).To(HaveKeyWithValue("tracknumber", []string{"2/10"}))
m = m.Map(e.CustomMappings())
Expect(m).To(HaveKeyWithValue("tracknumber", []string{"2/10", "2/10", "2"}))
m = mds["tests/fixtures/test.ogg"]
Expect(err).To(BeNil())
Expect(m).ToNot(HaveKey("title"))