Skip to content

Commit

Permalink
feat(subsonic): make it easier to add more tag reading backends
Browse files Browse the repository at this point in the history
related #379
related #324
related #244
  • Loading branch information
sentriz committed Oct 2, 2023
1 parent ae82153 commit 8382f61
Show file tree
Hide file tree
Showing 18 changed files with 370 additions and 383 deletions.
3 changes: 2 additions & 1 deletion Dockerfile
Expand Up @@ -21,7 +21,8 @@ RUN apk add -U --no-cache \
mpv \
ca-certificates \
tzdata \
tini
tini \
shared-mime-info

COPY --from=builder \
/usr/lib/libgcc_s.so.1 \
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile.debug
Expand Up @@ -5,5 +5,6 @@ RUN apk add -U --no-cache \
git \
sqlite \
taglib-dev \
zlib-dev
zlib-dev \
shared-mime-info
WORKDIR /src
3 changes: 2 additions & 1 deletion Dockerfile.dev
Expand Up @@ -18,7 +18,8 @@ FROM alpine:3.18
RUN apk add -U --no-cache \
ffmpeg \
mpv \
ca-certificates
ca-certificates \
shared-mime-info
COPY --from=builder \
/usr/lib/libgcc_s.so.1 \
/usr/lib/libstdc++.so.6 \
Expand Down
14 changes: 10 additions & 4 deletions cmd/gonic/gonic.go
Expand Up @@ -35,7 +35,8 @@ import (
"go.senan.xyz/gonic/playlist"
"go.senan.xyz/gonic/podcasts"
"go.senan.xyz/gonic/scanner"
"go.senan.xyz/gonic/scanner/tags"
"go.senan.xyz/gonic/scanner/tags/tagcommon"
"go.senan.xyz/gonic/scanner/tags/taglib"
"go.senan.xyz/gonic/scrobble"
"go.senan.xyz/gonic/server/ctrladmin"
"go.senan.xyz/gonic/server/ctrlsubsonic"
Expand Down Expand Up @@ -167,18 +168,23 @@ func main() {
log.Printf(" %-25s %s\n", f.Name, value)
})

tagger := &tags.TagReader{}
tagReader := tagcommon.ChainReader{
taglib.TagLib{},
// ffprobe reader?
// nfo reader?
}

scannr := scanner.New(
ctrlsubsonic.MusicPaths(musicPaths),
dbc,
map[scanner.Tag]scanner.MultiValueSetting{
scanner.Genre: scanner.MultiValueSetting(confMultiValueGenre),
scanner.AlbumArtist: scanner.MultiValueSetting(confMultiValueAlbumArtist),
},
tagger,
tagReader,
*confExcludePatterns,
)
podcast := podcasts.New(dbc, *confPodcastPath, tagger)
podcast := podcasts.New(dbc, *confPodcastPath, tagReader)
transcoder := transcode.NewCachingTranscoder(
transcode.NewFFmpegTranscoder(),
cacheDirAudio,
Expand Down
3 changes: 1 addition & 2 deletions db/model.go
Expand Up @@ -3,13 +3,12 @@ package db

import (
"fmt"
"mime"
"path/filepath"
"sort"
"strings"
"time"

"go.senan.xyz/gonic/mime"

// TODO: remove this dep
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
)
Expand Down
43 changes: 0 additions & 43 deletions mime/mime.go

This file was deleted.

97 changes: 48 additions & 49 deletions mockfs/mockfs.go
Expand Up @@ -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")
Expand Down Expand Up @@ -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{
Expand All @@ -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)
Expand All @@ -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{})
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
})
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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]*TagInfo
}

type tagReader struct {
paths map[string]*tagReaderResult
func (m *tagReader) CanRead(absPath string) bool {
stat, _ := os.Stat(absPath)
return stat.Mode().IsRegular()
}

func (m *tagReader) Read(abspath string) (tags.Parser, error) {
p, ok := m.paths[abspath]
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 (m *Tags) Length() int { return firstInt(100, m.RawLength) }
func (m *Tags) Bitrate() int { return firstInt(100, m.RawBitrate) }

var _ tags.Parser = (*Tags)(nil)
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) }

var _ tagcommon.Reader = (*tagReader)(nil)

func firstInt(or int, ints ...int) int {
for _, int := range ints {
Expand Down

0 comments on commit 8382f61

Please sign in to comment.