Tweak SimilarSongs algorithm to prioritize the requested main artist

This commit is contained in:
Deluan
2021-10-24 18:04:40 -04:00
parent 18e1c169f9
commit 76fdcd112b
3 changed files with 25 additions and 16 deletions
+16 -7
View File
@@ -163,28 +163,37 @@ func (e *externalMetadata) SimilarSongs(ctx context.Context, id string, count in
return nil, ctx.Err() return nil, ctx.Err()
} }
artists := model.Artists{artist.Artist}
artists = append(artists, artist.SimilarArtists...)
weightedSongs := utils.NewWeightedRandomChooser() weightedSongs := utils.NewWeightedRandomChooser()
for _, a := range artists { addArtist := func(a model.Artist, weightedSongs *utils.WeightedChooser, count, artistWeight int) error {
if utils.IsCtxDone(ctx) { if utils.IsCtxDone(ctx) {
log.Warn(ctx, "SimilarSongs call canceled", ctx.Err()) log.Warn(ctx, "SimilarSongs call canceled", ctx.Err())
return nil, ctx.Err() return ctx.Err()
} }
topCount := utils.MaxInt(count, 20) topCount := utils.MaxInt(count, 20)
topSongs, err := e.getMatchingTopSongs(ctx, e.ag, &auxArtist{Name: a.Name, Artist: a}, topCount) topSongs, err := e.getMatchingTopSongs(ctx, e.ag, &auxArtist{Name: a.Name, Artist: a}, topCount)
if err != nil { if err != nil {
log.Warn(ctx, "Error getting artist's top songs", "artist", a.Name, err) log.Warn(ctx, "Error getting artist's top songs", "artist", a.Name, err)
continue return nil
} }
weight := topCount * 4 weight := topCount * (4 + artistWeight)
for _, mf := range topSongs { for _, mf := range topSongs {
weightedSongs.Put(mf, weight) weightedSongs.Put(mf, weight)
weight -= 4 weight -= 4
} }
return nil
}
err = addArtist(artist.Artist, weightedSongs, count, 10)
if err != nil {
return nil, err
}
for _, a := range artist.SimilarArtists {
err := addArtist(a, weightedSongs, count, 0)
if err != nil {
return nil, err
}
} }
var similarSongs model.MediaFiles var similarSongs model.MediaFiles
+8 -8
View File
@@ -6,29 +6,29 @@ import (
"time" "time"
) )
type weightedChooser struct { type WeightedChooser struct {
entries []interface{} entries []interface{}
weights []int weights []int
totalWeight int totalWeight int
rng *rand.Rand rng *rand.Rand
} }
func NewWeightedRandomChooser() *weightedChooser { func NewWeightedRandomChooser() *WeightedChooser {
src := rand.NewSource(time.Now().UTC().UnixNano()) src := rand.NewSource(time.Now().UTC().UnixNano())
return &weightedChooser{ return &WeightedChooser{
rng: rand.New(src), // nolint:gosec rng: rand.New(src), // nolint:gosec
} }
} }
func (w *weightedChooser) Put(value interface{}, weight int) { func (w *WeightedChooser) Put(value interface{}, weight int) {
w.entries = append(w.entries, value) w.entries = append(w.entries, value)
w.weights = append(w.weights, weight) w.weights = append(w.weights, weight)
w.totalWeight += weight w.totalWeight += weight
} }
// GetAndRemove choose a random entry based on their weights, and removes it from the list // GetAndRemove choose a random entry based on their weights, and removes it from the list
func (w *weightedChooser) GetAndRemove() (interface{}, error) { func (w *WeightedChooser) GetAndRemove() (interface{}, error) {
if w.totalWeight == 0 { if w.totalWeight == 0 {
return nil, errors.New("cannot choose from zero weight") return nil, errors.New("cannot choose from zero weight")
} }
@@ -42,7 +42,7 @@ func (w *weightedChooser) GetAndRemove() (interface{}, error) {
} }
// Based on https://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/ // Based on https://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/
func (w *weightedChooser) weightedChoice() (int, error) { func (w *WeightedChooser) weightedChoice() (int, error) {
rnd := w.rng.Intn(w.totalWeight) rnd := w.rng.Intn(w.totalWeight)
for i, weight := range w.weights { for i, weight := range w.weights {
rnd -= weight rnd -= weight
@@ -53,7 +53,7 @@ func (w *weightedChooser) weightedChoice() (int, error) {
return 0, errors.New("internal error - code should not reach this point") return 0, errors.New("internal error - code should not reach this point")
} }
func (w *weightedChooser) Remove(i int) { func (w *WeightedChooser) Remove(i int) {
w.totalWeight -= w.weights[i] w.totalWeight -= w.weights[i]
w.weights[i] = w.weights[len(w.weights)-1] w.weights[i] = w.weights[len(w.weights)-1]
@@ -64,6 +64,6 @@ func (w *weightedChooser) Remove(i int) {
w.entries = w.entries[:len(w.entries)-1] w.entries = w.entries[:len(w.entries)-1]
} }
func (w *weightedChooser) Size() int { func (w *WeightedChooser) Size() int {
return len(w.entries) return len(w.entries)
} }
+1 -1
View File
@@ -6,7 +6,7 @@ import (
) )
var _ = Describe("WeightedRandomChooser", func() { var _ = Describe("WeightedRandomChooser", func() {
var w *weightedChooser var w *WeightedChooser
BeforeEach(func() { BeforeEach(func() {
w = NewWeightedRandomChooser() w = NewWeightedRandomChooser()
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {