mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-03 20:47:35 +03:00
Move alternative tag names mapping to metadata
This commit is contained in:
parent
0c22af3585
commit
6f5aaa1ec4
7 changed files with 146 additions and 120 deletions
|
@ -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
|
||||
|
|
|
@ -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"))
|
||||
})
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
89
scanner/metadata/metadata_internal_test.go
Normal file
89
scanner/metadata/metadata_internal_test.go
Normal 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))
|
||||
})
|
||||
})
|
||||
})
|
|
@ -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))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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"))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue