Invert dependency of metadata and extractors
This commit is contained in:
@@ -11,19 +11,20 @@ import (
|
|||||||
|
|
||||||
"github.com/navidrome/navidrome/conf"
|
"github.com/navidrome/navidrome/conf"
|
||||||
"github.com/navidrome/navidrome/log"
|
"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)
|
args := e.createProbeCommand(files)
|
||||||
|
|
||||||
log.Trace("Executing command", "args", args)
|
log.Trace("Executing command", "args", args)
|
||||||
cmd := exec.Command(args[0], args[1:]...) // #nosec
|
cmd := exec.Command(args[0], args[1:]...) // #nosec
|
||||||
output, _ := cmd.CombinedOutput()
|
output, _ := cmd.CombinedOutput()
|
||||||
fileTags := map[string]parsedTags{}
|
fileTags := map[string]metadata.ParsedTags{}
|
||||||
if len(output) == 0 {
|
if len(output) == 0 {
|
||||||
return fileTags, errors.New("error extracting metadata files")
|
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
|
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)
|
tags := e.parseInfo(info)
|
||||||
if len(tags) == 0 {
|
if len(tags) == 0 {
|
||||||
log.Trace("Not a media file. Skipping", "filePath", filePath)
|
log.Trace("Not a media file. Skipping", "filePath", filePath)
|
||||||
@@ -83,7 +84,7 @@ var (
|
|||||||
coverRx = regexp.MustCompile(`^\s{2,4}Stream #\d+:\d+: (Video):.*`)
|
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{}
|
outputs := map[string]string{}
|
||||||
all := inputRegex.FindAllStringSubmatchIndex(output, -1)
|
all := inputRegex.FindAllStringSubmatchIndex(output, -1)
|
||||||
for i, loc := range all {
|
for i, loc := range all {
|
||||||
@@ -105,7 +106,7 @@ func (e *Parser) parseOutput(output string) map[string]string {
|
|||||||
return outputs
|
return outputs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Parser) parseInfo(info string) map[string][]string {
|
func (e *Extractor) parseInfo(info string) map[string][]string {
|
||||||
tags := map[string][]string{}
|
tags := map[string][]string{}
|
||||||
|
|
||||||
reader := strings.NewReader(info)
|
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)
|
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)
|
d, err := time.Parse("15:04:05", tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "0"
|
return "0"
|
||||||
@@ -184,7 +185,7 @@ func (e *Parser) parseDuration(tag string) string {
|
|||||||
return strconv.FormatFloat(d.Sub(zeroTime).Seconds(), 'f', 2, 32)
|
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" {
|
if tag == "mono" {
|
||||||
return "1"
|
return "1"
|
||||||
} else if tag == "stereo" {
|
} else if tag == "stereo" {
|
||||||
@@ -199,7 +200,7 @@ func (e *Parser) parseChannels(tag string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Inputs will always be absolute paths
|
// 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, " ")
|
split := strings.Split(conf.Server.ProbeCommand, " ")
|
||||||
args := make([]string, 0)
|
args := make([]string, 0)
|
||||||
|
|
||||||
@@ -214,3 +215,7 @@ func (e *Parser) createProbeCommand(inputs []string) []string {
|
|||||||
}
|
}
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
metadata.RegisterExtractor(ExtractorID, &Extractor{})
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Parser", func() {
|
var _ = Describe("Extractor", func() {
|
||||||
var e *Parser
|
var e *Extractor
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
e = &Parser{}
|
e = &Extractor{}
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("extractMetadata", func() {
|
Context("extractMetadata", func() {
|
||||||
|
|||||||
@@ -14,25 +14,26 @@ import (
|
|||||||
"github.com/navidrome/navidrome/conf"
|
"github.com/navidrome/navidrome/conf"
|
||||||
"github.com/navidrome/navidrome/consts"
|
"github.com/navidrome/navidrome/consts"
|
||||||
"github.com/navidrome/navidrome/log"
|
"github.com/navidrome/navidrome/log"
|
||||||
"github.com/navidrome/navidrome/scanner/metadata/ffmpeg"
|
|
||||||
"github.com/navidrome/navidrome/scanner/metadata/taglib"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Parser interface {
|
type ParsedTags = map[string][]string
|
||||||
Parse(files ...string) (map[string]map[string][]string, error)
|
|
||||||
|
type Extractor interface {
|
||||||
|
Parse(files ...string) (map[string]ParsedTags, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsers = map[string]Parser{
|
var extractors = map[string]Extractor{}
|
||||||
"ffmpeg": &ffmpeg.Parser{},
|
|
||||||
"taglib": &taglib.Parser{},
|
func RegisterExtractor(id string, parser Extractor) {
|
||||||
|
extractors[id] = parser
|
||||||
}
|
}
|
||||||
|
|
||||||
func Extract(files ...string) (map[string]Tags, error) {
|
func Extract(files ...string) (map[string]Tags, error) {
|
||||||
p, ok := parsers[conf.Server.Scanner.Extractor]
|
p, ok := extractors[conf.Server.Scanner.Extractor]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warn("Invalid 'Scanner.Extractor' option. Using default", "requested", conf.Server.Scanner.Extractor,
|
log.Warn("Invalid 'Scanner.Extractor' option. Using default", "requested", conf.Server.Scanner.Extractor,
|
||||||
"validOptions", "ffmpeg,taglib", "default", consts.DefaultScannerExtractor)
|
"validOptions", "ffmpeg,taglib", "default", consts.DefaultScannerExtractor)
|
||||||
p = parsers[consts.DefaultScannerExtractor]
|
p = extractors[consts.DefaultScannerExtractor]
|
||||||
}
|
}
|
||||||
|
|
||||||
extractedTags, err := p.Parse(files...)
|
extractedTags, err := p.Parse(files...)
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/navidrome/navidrome/log"
|
"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) {
|
func (e *Extractor) Parse(paths ...string) (map[string]metadata.ParsedTags, error) {
|
||||||
fileTags := map[string]parsedTags{}
|
fileTags := map[string]metadata.ParsedTags{}
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
tags, err := e.extractMetadata(path)
|
tags, err := e.extractMetadata(path)
|
||||||
if !errors.Is(err, os.ErrPermission) {
|
if !errors.Is(err, os.ErrPermission) {
|
||||||
@@ -23,7 +24,7 @@ func (e *Parser) Parse(paths ...string) (map[string]parsedTags, error) {
|
|||||||
return fileTags, nil
|
return fileTags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Parser) extractMetadata(filePath string) (parsedTags, error) {
|
func (e *Extractor) extractMetadata(filePath string) (metadata.ParsedTags, error) {
|
||||||
tags, err := Read(filePath)
|
tags, err := Read(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("TagLib: Error reading metadata from file. Skipping", "filePath", filePath, err)
|
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
|
return tags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
metadata.RegisterExtractor(ExtractorID, &Extractor{})
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Parser", func() {
|
var _ = Describe("Extractor", func() {
|
||||||
var e *Parser
|
var e *Extractor
|
||||||
// This file will have 0222 (no read) permission during these tests
|
// This file will have 0222 (no read) permission during these tests
|
||||||
var accessForbiddenFile = "tests/fixtures/test_no_read_permission.ogg"
|
var accessForbiddenFile = "tests/fixtures/test_no_read_permission.ogg"
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
e = &Parser{}
|
e = &Extractor{}
|
||||||
|
|
||||||
err := os.Chmod(accessForbiddenFile, 0222)
|
err := os.Chmod(accessForbiddenFile, 0222)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import (
|
|||||||
"github.com/navidrome/navidrome/model"
|
"github.com/navidrome/navidrome/model"
|
||||||
"github.com/navidrome/navidrome/model/request"
|
"github.com/navidrome/navidrome/model/request"
|
||||||
"github.com/navidrome/navidrome/scanner/metadata"
|
"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"
|
"github.com/navidrome/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user