feat(subsonic): make it easier to add more tag reading backends

related https://github.com/sentriz/gonic/issues/379
related https://github.com/sentriz/gonic/issues/324
related https://github.com/sentriz/gonic/issues/244
This commit is contained in:
sentriz
2023-10-02 20:02:38 +01:00
parent ae82153d79
commit 8382f6123c
18 changed files with 370 additions and 383 deletions

View File

@@ -15,7 +15,7 @@ import (
"github.com/mattn/go-sqlite3"
"go.senan.xyz/gonic/db"
"go.senan.xyz/gonic/scanner"
"go.senan.xyz/gonic/scanner/tags"
"go.senan.xyz/gonic/scanner/tags/tagcommon"
)
var ErrPathNotFound = errors.New("path not found")
@@ -69,7 +69,7 @@ func newMockFS(tb testing.TB, dirs []string, excludePattern string) *MockFS {
scanner.AlbumArtist: {Mode: scanner.Multi},
}
tagReader := &tagReader{paths: map[string]*tagReaderResult{}}
tagReader := &tagReader{paths: map[string]*TagInfo{}}
scanner := scanner.New(absDirs, dbc, multiValueSettings, tagReader, excludePattern)
return &MockFS{
@@ -81,11 +81,13 @@ func newMockFS(tb testing.TB, dirs []string, excludePattern string) *MockFS {
}
}
func (m *MockFS) DB() *db.DB { return m.db }
func (m *MockFS) TmpDir() string { return m.dir }
func (m *MockFS) TagReader() tags.Reader { return m.tagReader }
func (m *MockFS) DB() *db.DB { return m.db }
func (m *MockFS) TmpDir() string { return m.dir }
func (m *MockFS) TagReader() tagcommon.Reader { return m.tagReader }
func (m *MockFS) ScanAndClean() *scanner.Context {
m.t.Helper()
ctx, err := m.scanner.ScanAndClean(scanner.ScanOptions{})
if err != nil {
m.t.Fatalf("error scan and cleaning: %v", err)
@@ -94,6 +96,8 @@ func (m *MockFS) ScanAndClean() *scanner.Context {
}
func (m *MockFS) ScanAndCleanErr() (*scanner.Context, error) {
m.t.Helper()
return m.scanner.ScanAndClean(scanner.ScanOptions{})
}
@@ -126,12 +130,11 @@ func (m *MockFS) addItems(prefix string, onlyGlob string, covers bool) {
}
m.AddTrack(path)
m.SetTags(path, func(tags *Tags) error {
m.SetTags(path, func(tags *TagInfo) {
tags.RawArtist = fmt.Sprintf("artist-%d", ar)
tags.RawAlbumArtist = fmt.Sprintf("artist-%d", ar)
tags.RawAlbum = fmt.Sprintf("album-%d", al)
tags.RawTitle = fmt.Sprintf("title-%d", tr)
return nil
})
}
if covers {
@@ -180,10 +183,9 @@ func (m *MockFS) SetRealAudio(path string, length int, audioPath string) {
if err := os.Symlink(filepath.Join(wd, audioPath), abspath); err != nil {
m.t.Fatalf("symlink: %v", err)
}
m.SetTags(path, func(tags *Tags) error {
m.SetTags(path, func(tags *TagInfo) {
tags.RawLength = length
tags.RawBitrate = 0
return nil
})
}
@@ -288,18 +290,15 @@ func (m *MockFS) AddCover(path string) {
defer f.Close()
}
func (m *MockFS) SetTags(path string, cb func(*Tags) error) {
abspath := filepath.Join(m.dir, path)
if err := os.Chtimes(abspath, time.Time{}, time.Now()); err != nil {
func (m *MockFS) SetTags(path string, cb func(*TagInfo)) {
absPath := filepath.Join(m.dir, path)
if err := os.Chtimes(absPath, time.Time{}, time.Now()); err != nil {
m.t.Fatalf("touch track: %v", err)
}
r := m.tagReader
if _, ok := r.paths[abspath]; !ok {
r.paths[abspath] = &tagReaderResult{tags: &Tags{}}
}
if err := cb(r.paths[abspath].tags); err != nil {
r.paths[abspath].err = err
if _, ok := m.tagReader.paths[absPath]; !ok {
m.tagReader.paths[absPath] = &TagInfo{}
}
cb(m.tagReader.paths[absPath])
}
func (m *MockFS) DumpDB(suffix ...string) {
@@ -353,54 +352,54 @@ func (m *MockFS) DumpDB(suffix ...string) {
m.t.Error(destPath)
}
type tagReaderResult struct {
tags *Tags
err error
}
type tagReader struct {
paths map[string]*tagReaderResult
paths map[string]*TagInfo
}
func (m *tagReader) Read(abspath string) (tags.Parser, error) {
p, ok := m.paths[abspath]
func (m *tagReader) CanRead(absPath string) bool {
stat, _ := os.Stat(absPath)
return stat.Mode().IsRegular()
}
func (m *tagReader) Read(absPath string) (tagcommon.Info, error) {
p, ok := m.paths[absPath]
if !ok {
return nil, ErrPathNotFound
}
return p.tags, p.err
if p.Error != nil {
return nil, p.Error
}
return p, nil
}
var _ tags.Reader = (*tagReader)(nil)
type Tags struct {
type TagInfo struct {
RawTitle string
RawArtist string
RawAlbum string
RawAlbumArtist string
RawAlbumArtists []string
RawGenre string
RawBitrate int
RawLength int
RawBitrate int
RawLength int
Error error
}
func (m *Tags) Title() string { return m.RawTitle }
func (m *Tags) BrainzID() string { return "" }
func (m *Tags) Artist() string { return m.RawArtist }
func (m *Tags) Album() string { return m.RawAlbum }
func (m *Tags) AlbumArtist() string { return m.RawAlbumArtist }
func (m *Tags) AlbumArtists() []string { return m.RawAlbumArtists }
func (m *Tags) AlbumBrainzID() string { return "" }
func (m *Tags) Genre() string { return m.RawGenre }
func (m *Tags) Genres() []string { return []string{m.RawGenre} }
func (m *Tags) TrackNumber() int { return 1 }
func (m *Tags) DiscNumber() int { return 1 }
func (m *Tags) Year() int { return 2021 }
func (i *TagInfo) Title() string { return i.RawTitle }
func (i *TagInfo) BrainzID() string { return "" }
func (i *TagInfo) Artist() string { return i.RawArtist }
func (i *TagInfo) Album() string { return i.RawAlbum }
func (i *TagInfo) AlbumArtist() string { return i.RawAlbumArtist }
func (i *TagInfo) AlbumArtists() []string { return i.RawAlbumArtists }
func (i *TagInfo) AlbumBrainzID() string { return "" }
func (i *TagInfo) Genre() string { return i.RawGenre }
func (i *TagInfo) Genres() []string { return []string{i.RawGenre} }
func (i *TagInfo) TrackNumber() int { return 1 }
func (i *TagInfo) DiscNumber() int { return 1 }
func (i *TagInfo) Year() int { return 2021 }
func (i *TagInfo) Length() int { return firstInt(100, i.RawLength) }
func (i *TagInfo) Bitrate() int { return firstInt(100, i.RawBitrate) }
func (m *Tags) Length() int { return firstInt(100, m.RawLength) }
func (m *Tags) Bitrate() int { return firstInt(100, m.RawBitrate) }
var _ tags.Parser = (*Tags)(nil)
var _ tagcommon.Reader = (*tagReader)(nil)
func firstInt(or int, ints ...int) int {
for _, int := range ints {