navidrome/persistence/helpers.go

90 lines
2.1 KiB
Go

package persistence
import (
"context"
"encoding/json"
"fmt"
"regexp"
"strings"
"github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
)
func toSqlArgs(rec interface{}) (map[string]interface{}, error) {
// Convert to JSON...
b, err := json.Marshal(rec)
if err != nil {
return nil, err
}
// ... then convert to map
var m map[string]interface{}
err = json.Unmarshal(b, &m)
r := make(map[string]interface{}, len(m))
for f, v := range m {
isAnnotationField := utils.StringInSlice(f, model.AnnotationFields)
isBookmarkField := utils.StringInSlice(f, model.BookmarkFields)
if !isAnnotationField && !isBookmarkField && v != nil {
r[toSnakeCase(f)] = v
}
}
return r, err
}
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
func toSnakeCase(str string) string {
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
return strings.ToLower(snake)
}
func exists(subTable string, cond squirrel.Sqlizer) existsCond {
return existsCond{subTable: subTable, cond: cond}
}
type existsCond struct {
subTable string
cond squirrel.Sqlizer
}
func (e existsCond) ToSql() (string, []interface{}, error) {
sql, args, err := e.cond.ToSql()
sql = fmt.Sprintf("exists (select 1 from %s where %s)", e.subTable, sql)
return sql, args, err
}
func getMbzId(ctx context.Context, mbzIDS, entityName, name string) string {
ids := strings.Fields(mbzIDS)
if len(ids) == 0 {
return ""
}
idCounts := map[string]int{}
for _, id := range ids {
if c, ok := idCounts[id]; ok {
idCounts[id] = c + 1
} else {
idCounts[id] = 1
}
}
var topKey string
var topCount int
for k, v := range idCounts {
if v > topCount {
topKey = k
topCount = v
}
}
if len(idCounts) > 1 && name != consts.VariousArtists {
log.Warn(ctx, "Multiple MBIDs found for "+entityName, "name", name, "mbids", idCounts)
}
return topKey
}