diff --git a/scanner/metadata/ffmpeg/ffmpeg.go b/scanner/metadata/ffmpeg/ffmpeg.go index 21a9c57b2..d26e8f5f4 100644 --- a/scanner/metadata/ffmpeg/ffmpeg.go +++ b/scanner/metadata/ffmpeg/ffmpeg.go @@ -11,19 +11,20 @@ import ( "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/log" + "github.com/navidrome/navidrome/scanner/metadata" ) -type Parser struct{} +const ExtractorID = "ffmpeg" -type parsedTags = map[string][]string +type Extractor struct{} -func (e *Parser) Parse(files ...string) (map[string]parsedTags, error) { +func (e *Extractor) Parse(files ...string) (map[string]metadata.ParsedTags, error) { args := e.createProbeCommand(files) log.Trace("Executing command", "args", args) cmd := exec.Command(args[0], args[1:]...) // #nosec output, _ := cmd.CombinedOutput() - fileTags := map[string]parsedTags{} + fileTags := map[string]metadata.ParsedTags{} if len(output) == 0 { return fileTags, errors.New("error extracting metadata files") } @@ -38,7 +39,7 @@ func (e *Parser) Parse(files ...string) (map[string]parsedTags, error) { return fileTags, nil } -func (e *Parser) extractMetadata(filePath, info string) (parsedTags, error) { +func (e *Extractor) extractMetadata(filePath, info string) (metadata.ParsedTags, error) { tags := e.parseInfo(info) if len(tags) == 0 { log.Trace("Not a media file. Skipping", "filePath", filePath) @@ -83,7 +84,7 @@ var ( coverRx = regexp.MustCompile(`^\s{2,4}Stream #\d+:\d+: (Video):.*`) ) -func (e *Parser) parseOutput(output string) map[string]string { +func (e *Extractor) parseOutput(output string) map[string]string { outputs := map[string]string{} all := inputRegex.FindAllStringSubmatchIndex(output, -1) for i, loc := range all { @@ -105,7 +106,7 @@ func (e *Parser) parseOutput(output string) map[string]string { return outputs } -func (e *Parser) parseInfo(info string) map[string][]string { +func (e *Extractor) parseInfo(info string) map[string][]string { tags := map[string][]string{} reader := strings.NewReader(info) @@ -176,7 +177,7 @@ func (e *Parser) parseInfo(info string) map[string][]string { var zeroTime = time.Date(0000, time.January, 1, 0, 0, 0, 0, time.UTC) -func (e *Parser) parseDuration(tag string) string { +func (e *Extractor) parseDuration(tag string) string { d, err := time.Parse("15:04:05", tag) if err != nil { return "0" @@ -184,7 +185,7 @@ func (e *Parser) parseDuration(tag string) string { return strconv.FormatFloat(d.Sub(zeroTime).Seconds(), 'f', 2, 32) } -func (e *Parser) parseChannels(tag string) string { +func (e *Extractor) parseChannels(tag string) string { if tag == "mono" { return "1" } else if tag == "stereo" { @@ -199,7 +200,7 @@ func (e *Parser) parseChannels(tag string) string { } // Inputs will always be absolute paths -func (e *Parser) createProbeCommand(inputs []string) []string { +func (e *Extractor) createProbeCommand(inputs []string) []string { split := strings.Split(conf.Server.ProbeCommand, " ") args := make([]string, 0) @@ -214,3 +215,7 @@ func (e *Parser) createProbeCommand(inputs []string) []string { } return args } + +func init() { + metadata.RegisterExtractor(ExtractorID, &Extractor{}) +} diff --git a/scanner/metadata/ffmpeg/ffmpeg_test.go b/scanner/metadata/ffmpeg/ffmpeg_test.go index af5852735..71fe61691 100644 --- a/scanner/metadata/ffmpeg/ffmpeg_test.go +++ b/scanner/metadata/ffmpeg/ffmpeg_test.go @@ -5,10 +5,10 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("Parser", func() { - var e *Parser +var _ = Describe("Extractor", func() { + var e *Extractor BeforeEach(func() { - e = &Parser{} + e = &Extractor{} }) Context("extractMetadata", func() { diff --git a/scanner/metadata/metadata.go b/scanner/metadata/metadata.go index d2b4f0a86..cf6caea4c 100644 --- a/scanner/metadata/metadata.go +++ b/scanner/metadata/metadata.go @@ -14,25 +14,26 @@ import ( "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/log" - "github.com/navidrome/navidrome/scanner/metadata/ffmpeg" - "github.com/navidrome/navidrome/scanner/metadata/taglib" ) -type Parser interface { - Parse(files ...string) (map[string]map[string][]string, error) +type ParsedTags = map[string][]string + +type Extractor interface { + Parse(files ...string) (map[string]ParsedTags, error) } -var parsers = map[string]Parser{ - "ffmpeg": &ffmpeg.Parser{}, - "taglib": &taglib.Parser{}, +var extractors = map[string]Extractor{} + +func RegisterExtractor(id string, parser Extractor) { + extractors[id] = parser } func Extract(files ...string) (map[string]Tags, error) { - p, ok := parsers[conf.Server.Scanner.Extractor] + p, ok := extractors[conf.Server.Scanner.Extractor] if !ok { log.Warn("Invalid 'Scanner.Extractor' option. Using default", "requested", conf.Server.Scanner.Extractor, "validOptions", "ffmpeg,taglib", "default", consts.DefaultScannerExtractor) - p = parsers[consts.DefaultScannerExtractor] + p = extractors[consts.DefaultScannerExtractor] } extractedTags, err := p.Parse(files...) diff --git a/scanner/metadata/taglib/taglib.go b/scanner/metadata/taglib/taglib.go index 6df5aa11e..764171625 100644 --- a/scanner/metadata/taglib/taglib.go +++ b/scanner/metadata/taglib/taglib.go @@ -6,14 +6,15 @@ import ( "strconv" "github.com/navidrome/navidrome/log" + "github.com/navidrome/navidrome/scanner/metadata" ) -type Parser struct{} +const ExtractorID = "taglib" -type parsedTags = map[string][]string +type Extractor struct{} -func (e *Parser) Parse(paths ...string) (map[string]parsedTags, error) { - fileTags := map[string]parsedTags{} +func (e *Extractor) Parse(paths ...string) (map[string]metadata.ParsedTags, error) { + fileTags := map[string]metadata.ParsedTags{} for _, path := range paths { tags, err := e.extractMetadata(path) if !errors.Is(err, os.ErrPermission) { @@ -23,7 +24,7 @@ func (e *Parser) Parse(paths ...string) (map[string]parsedTags, error) { return fileTags, nil } -func (e *Parser) extractMetadata(filePath string) (parsedTags, error) { +func (e *Extractor) extractMetadata(filePath string) (metadata.ParsedTags, error) { tags, err := Read(filePath) if err != nil { log.Warn("TagLib: Error reading metadata from file. Skipping", "filePath", filePath, err) @@ -53,3 +54,7 @@ func (e *Parser) extractMetadata(filePath string) (parsedTags, error) { } return tags, nil } + +func init() { + metadata.RegisterExtractor(ExtractorID, &Extractor{}) +} diff --git a/scanner/metadata/taglib/taglib_test.go b/scanner/metadata/taglib/taglib_test.go index 4ce228e23..b5308edb6 100644 --- a/scanner/metadata/taglib/taglib_test.go +++ b/scanner/metadata/taglib/taglib_test.go @@ -8,13 +8,13 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("Parser", func() { - var e *Parser +var _ = Describe("Extractor", func() { + var e *Extractor // This file will have 0222 (no read) permission during these tests var accessForbiddenFile = "tests/fixtures/test_no_read_permission.ogg" BeforeEach(func() { - e = &Parser{} + e = &Extractor{} err := os.Chmod(accessForbiddenFile, 0222) Expect(err).ToNot(HaveOccurred()) diff --git a/scanner/tag_scanner.go b/scanner/tag_scanner.go index 83aa1d8e2..278e3d653 100644 --- a/scanner/tag_scanner.go +++ b/scanner/tag_scanner.go @@ -16,6 +16,8 @@ import ( "github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/model/request" "github.com/navidrome/navidrome/scanner/metadata" + _ "github.com/navidrome/navidrome/scanner/metadata/ffmpeg" + _ "github.com/navidrome/navidrome/scanner/metadata/taglib" "github.com/navidrome/navidrome/utils" )