implemented recursive dedupe and compile

This commit is contained in:
Hazel Noack 2025-10-07 13:05:52 +02:00
parent 6fd2478359
commit bd62068e09
4 changed files with 243 additions and 4 deletions

View File

@ -1,6 +1,56 @@
package data package data
import "strconv"
type MusicObject interface { type MusicObject interface {
Compile() MusicObject
GetIndices() []string
Merge(other MusicObject) MusicObject
}
func dedupeMusicObjects[T MusicObject](inputMusicObjects []T) []T {
indexMapping := map[string]int{}
deduped := []T{}
for _, musicObject := range inputMusicObjects {
indices := musicObject.GetIndices()
// Check if we've seen any of these indices before
found := false
var existingIndex int
for _, index := range indices {
if idx, exists := indexMapping[index]; exists {
found = true
existingIndex = idx
break
}
}
if found {
// Merge with existing object
existing := deduped[existingIndex]
merged := existing.Merge(musicObject).(T)
deduped[existingIndex] = merged
// Update indices for the merged object
newIndices := merged.GetIndices()
for _, index := range newIndices {
indexMapping[index] = existingIndex
}
} else {
// Add new object
deduped = append(deduped, musicObject)
newIndex := len(deduped) - 1
// Register all indices for this object
for _, index := range indices {
indexMapping[index] = newIndex
}
}
}
return deduped
} }
type Song struct { type Song struct {
@ -15,6 +65,44 @@ type Song struct {
Sources []Source Sources []Source
} }
func (m Song) GetIndices() []string {
res := sourceIndices(m.Sources)
if m.UnifiedName != "" {
res = append(res, "name"+m.UnifiedName)
}
if m.Id != 0 {
res = append(res, "id"+strconv.Itoa(m.Id))
}
return res
}
func (m Song) Merge(other MusicObject) MusicObject {
otherSong, ok := other.(Song)
if !ok {
return m
}
if m.Id == 0 && otherSong.Id != 0 {
m.Id = otherSong.Id
}
if m.Name == "" && otherSong.Name != "" {
m.Name = otherSong.Name
m.UnifiedName = otherSong.UnifiedName
}
m.Album = m.Album.Merge(otherSong.Album).(Album)
m.Sources = dedupeSources(append(m.Sources, otherSong.Sources...))
return m
}
func (m Song) Compile() MusicObject {
m.Sources = dedupeSources(m.Sources)
m.Artists = dedupeMusicObjects(m.Artists)
return m
}
type Album struct { type Album struct {
Id int Id int
@ -27,6 +115,46 @@ type Album struct {
Sources []Source Sources []Source
} }
func (m Album) GetIndices() []string {
res := sourceIndices(m.Sources)
if m.UnifiedName != "" {
res = append(res, "name"+m.UnifiedName)
}
if m.Id != 0 {
res = append(res, "id"+strconv.Itoa(m.Id))
}
return res
}
func (m Album) Merge(other MusicObject) MusicObject {
otherAlbum, ok := other.(Album)
if !ok {
return m
}
if m.Id == 0 && otherAlbum.Id != 0 {
m.Id = otherAlbum.Id
}
if m.Name == "" && otherAlbum.Name != "" {
m.Name = otherAlbum.Name
m.UnifiedName = otherAlbum.UnifiedName
}
m.Songs = dedupeMusicObjects(append(m.Songs, otherAlbum.Songs...))
m.Artists = dedupeMusicObjects(append(m.Artists, otherAlbum.Artists...))
m.Sources = dedupeSources(append(m.Sources, otherAlbum.Sources...))
return m
}
func (m Album) Compile() MusicObject {
m.Sources = dedupeSources(m.Sources)
m.Songs = dedupeMusicObjects(m.Songs)
m.Artists = dedupeMusicObjects(m.Artists)
return m
}
type Artist struct { type Artist struct {
Id int Id int
@ -37,3 +165,40 @@ type Artist struct {
Sources []Source Sources []Source
} }
func (m Artist) Merge(other MusicObject) MusicObject {
otherArtist, ok := other.(Artist)
if !ok {
return m
}
if m.Id == 0 && otherArtist.Id != 0 {
m.Id = otherArtist.Id
}
if m.Name == "" && otherArtist.Name != "" {
m.Name = otherArtist.Name
m.UnifiedName = otherArtist.UnifiedName
}
m.Albums = dedupeMusicObjects(append(m.Albums, otherArtist.Albums...))
m.Sources = dedupeSources(append(m.Sources, otherArtist.Sources...))
return m
}
func (m Artist) GetIndices() []string {
res := sourceIndices(m.Sources)
if m.UnifiedName != "" {
res = append(res, "name"+m.UnifiedName)
}
if m.Id != 0 {
res = append(res, "id"+strconv.Itoa(m.Id))
}
return res
}
func (m Artist) Compile() MusicObject {
m.Sources = dedupeSources(m.Sources)
m.Albums = dedupeMusicObjects(m.Albums)
return m
}

View File

@ -37,3 +37,38 @@ type Source struct {
SourceType *SourceType SourceType *SourceType
ObjectType ObjectType ObjectType ObjectType
} }
func dedupeSources(inputSources []Source) []Source {
urlMapping := map[string]int{}
deduped := []Source{}
for _, source := range inputSources {
if mergeWithIndex, ok := urlMapping[source.Url]; ok {
// has to merge current source with source at index
if source.ObjectType != "" {
deduped[mergeWithIndex].ObjectType = source.ObjectType
}
if source.SourceType != nil {
deduped[mergeWithIndex].SourceType = source.SourceType
}
} else {
// just appending
urlMapping[source.Url] = len(deduped)
deduped = append(deduped, source)
}
}
return deduped
}
func sourceIndices(sources []Source) []string {
res := []string{}
for _, source := range sources {
res = append(res, "url"+source.Url)
}
return res
}

View File

@ -60,6 +60,10 @@ func compileSource(source data.Source) (data.Source, error) {
} }
// find what object this source corresponds to // find what object this source corresponds to
if source.ObjectType != "" {
return source, nil
}
sourceType := source.SourceType sourceType := source.SourceType
if sourceType.RegexSong.MatchString(source.Url) { if sourceType.RegexSong.MatchString(source.Url) {
@ -80,7 +84,24 @@ func compileSource(source data.Source) (data.Source, error) {
return source, errors.New("couldn't find corresponding object source on " + sourceType.Name + " for " + source.Url) return source, errors.New("couldn't find corresponding object source on " + sourceType.Name + " for " + source.Url)
} }
func dedupeSources(sources []data.Source) []data.Source {
urls := map[string]interface{}{}
deduped := []data.Source{}
for _, raw := range sources {
parsed, _ := compileSource(raw)
if _, u := urls[parsed.Url]; !u {
urls[parsed.Url] = true
deduped = append(deduped, parsed)
}
}
return deduped
}
func Fetch(source data.Source) (data.MusicObject, error) { func Fetch(source data.Source) (data.MusicObject, error) {
// the fetch function without the post processing of the music objects
source, err := compileSource(source) source, err := compileSource(source)
if err != nil { if err != nil {
return nil, err return nil, err
@ -92,15 +113,33 @@ func Fetch(source data.Source) (data.MusicObject, error) {
} }
if source.ObjectType == data.SongSource { if source.ObjectType == data.SongSource {
return plugin.FetchSong(source) song, err := plugin.FetchSong(source)
if err != nil {
return song, err
}
song.Sources = dedupeSources(append(song.Sources, source))
return song, nil
} }
if source.ObjectType == data.AlbumSource { if source.ObjectType == data.AlbumSource {
return plugin.FetchAlbum(source) album, err := plugin.FetchAlbum(source)
if err != nil {
return album, err
}
album.Sources = dedupeSources(append(album.Sources, source))
return album, nil
} }
if source.ObjectType == data.ArtistSource { if source.ObjectType == data.ArtistSource {
return plugin.FetchArtist(source) artist, err := plugin.FetchArtist(source)
if err != nil {
return artist, err
}
artist.Sources = dedupeSources(append(artist.Sources, source))
return artist, nil
} }
return nil, nil return nil, nil

View File

@ -8,5 +8,5 @@ import (
func main() { func main() {
fmt.Println("music kraken") fmt.Println("music kraken")
data.GetSourceType("Youtube") fmt.Println(data.Source{}.SourceType == nil)
} }