navidrome/model/metadata/persistent_ids.go

101 lines
2.9 KiB
Go

package metadata
import (
"cmp"
"path/filepath"
"strings"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/id"
"github.com/navidrome/navidrome/utils"
"github.com/navidrome/navidrome/utils/slice"
"github.com/navidrome/navidrome/utils/str"
)
type hashFunc = func(...string) string
// getPID returns the persistent ID for a given spec, getting the referenced values from the metadata
// The spec is a pipe-separated list of fields, where each field is a comma-separated list of attributes
// Attributes can be either tags or some processed values like folder, albumid, albumartistid, etc.
// For each field, it gets all its attributes values and concatenates them, then hashes the result.
// If a field is empty, it is skipped and the function looks for the next field.
func createGetPID(hash hashFunc) func(mf model.MediaFile, md Metadata, spec string) string {
var getPID func(mf model.MediaFile, md Metadata, spec string) string
getAttr := func(mf model.MediaFile, md Metadata, attr string) string {
attr = strings.TrimSpace(strings.ToLower(attr))
switch attr {
case "albumid":
return getPID(mf, md, conf.Server.PID.Album)
case "folder":
return filepath.Dir(mf.Path)
case "albumartistid":
return hash(str.Clear(strings.ToLower(mf.AlbumArtist)))
case "title":
return mf.Title
case "album":
return str.Clear(strings.ToLower(md.String(model.TagAlbum)))
}
return md.String(model.TagName(attr))
}
getPID = func(mf model.MediaFile, md Metadata, spec string) string {
pid := ""
fields := strings.Split(spec, "|")
for _, field := range fields {
attributes := strings.Split(field, ",")
hasValue := false
values := slice.Map(attributes, func(attr string) string {
v := getAttr(mf, md, attr)
if v != "" {
hasValue = true
}
return v
})
if hasValue {
pid += strings.Join(values, "\\")
break
}
}
return hash(pid)
}
return func(mf model.MediaFile, md Metadata, spec string) string {
switch spec {
case "track_legacy":
return legacyTrackID(mf)
case "album_legacy":
return legacyAlbumID(md)
}
return getPID(mf, md, spec)
}
}
func (md Metadata) trackPID(mf model.MediaFile) string {
return createGetPID(id.NewHash)(mf, md, conf.Server.PID.Track)
}
func (md Metadata) albumID(mf model.MediaFile) string {
return createGetPID(id.NewHash)(mf, md, conf.Server.PID.Album)
}
// BFR Must be configurable?
func (md Metadata) artistID(name string) string {
mf := model.MediaFile{AlbumArtist: name}
return createGetPID(id.NewHash)(mf, md, "albumartistid")
}
func (md Metadata) mapTrackTitle() string {
if title := md.String(model.TagTitle); title != "" {
return title
}
return utils.BaseName(md.FilePath())
}
func (md Metadata) mapAlbumName() string {
return cmp.Or(
md.String(model.TagAlbum),
consts.UnknownAlbum,
)
}