package plugin import ( "errors" "regexp" "gitea.elara.ws/Hazel/music-kraken/internal/common" "gitea.elara.ws/Hazel/music-kraken/internal/data" ) type Plugin interface { Name() string Regex() *regexp.Regexp RegexArtist() *regexp.Regexp RegexAlbum() *regexp.Regexp RegexSong() *regexp.Regexp Init() Search(query common.Query) ([]data.MusicObject, error) FetchArtist(source data.Source) (data.Artist, error) FetchAlbum(source data.Source) (data.Album, error) FetchSong(source data.Source) (data.Song, error) } var namePlugins = map[string]Plugin{} var NameSourceType = map[string]data.SourceType{} func RegisterPlugin(plugin Plugin) error { name := plugin.Name() if _, ok := namePlugins[name]; ok { return errors.New("plugin " + name + " is already registered") } NameSourceType[name] = data.SourceType{ Name: name, Regex: plugin.Regex(), RegexArtist: plugin.RegexArtist(), RegexAlbum: plugin.RegexAlbum(), RegexSong: plugin.RegexSong(), } namePlugins[name] = plugin plugin.Init() return nil } func compileSourceType(source data.Source) (data.Source, error) { if source.SourceType != nil { if _, ok := namePlugins[source.SourceType.Name]; !ok { return source, errors.New("source type " + source.SourceType.Name + " not found") } return source, nil } for _, st := range NameSourceType { if m := st.Regex.FindString(source.Url); m != "" { source.Url = m source.SourceType = &st return source, nil } } return source, errors.New("couldn't find source type for " + source.Url) } func compileSource(source data.Source) (data.Source, error) { source, err := compileSourceType(source) if err != nil { return source, err } // find what object this source corresponds to if source.ObjectType != "" { return source, nil } sourceType := source.SourceType if sourceType.RegexSong.MatchString(source.Url) { source.ObjectType = data.SongSource return source, nil } if sourceType.RegexAlbum.MatchString(source.Url) { source.ObjectType = data.AlbumSource return source, nil } if sourceType.RegexArtist.MatchString(source.Url) { source.ObjectType = data.ArtistSource return source, nil } return source, errors.New("couldn't find corresponding object source on " + sourceType.Name + " for " + source.Url) } func FetchSource(source data.Source) (data.MusicObject, error) { // the fetch function without the post processing of the music objects source, err := compileSource(source) if err != nil { return nil, err } plugin, ok := namePlugins[source.SourceType.Name] if !ok { return nil, errors.New("didn't find plugin of the name " + source.SourceType.Name) } if source.ObjectType == data.SongSource { song, err := plugin.FetchSong(source) if err != nil { return song, err } song.Sources = append(song.Sources, source) song = song.Compile().(data.Song) return song, nil } if source.ObjectType == data.AlbumSource { album, err := plugin.FetchAlbum(source) if err != nil { return album, err } album.Sources = append(album.Sources, source) album = album.Compile().(data.Album) return album, nil } if source.ObjectType == data.ArtistSource { artist, err := plugin.FetchArtist(source) if err != nil { return artist, err } artist.Sources = append(artist.Sources, source) artist = artist.Compile().(data.Artist) return artist, nil } return nil, nil } func Fetch(musicObject data.MusicObject) (data.MusicObject, error) { sources := musicObject.GetSources() if len(sources) <= 0 { return musicObject, errors.New("didn't find a source for object") } for _, source := range sources { newMusicObject, err := FetchSource(source) if err != nil { return musicObject, err } musicObject = musicObject.Merge(newMusicObject) } return musicObject, nil } func FetchList(musicObject data.MusicObject) ([]data.MusicObject, error) { res := []data.MusicObject{} musicObject, err := Fetch(musicObject) if err != nil { return res, err } if a, ok := musicObject.(data.Song); ok { for _, ar := range a.Artists { res = append(res, ar) } if a.Album.Name != "" { res = append(res, a.Album, a) } } else if a, ok := musicObject.(data.Album); ok { for _, ar := range a.Artists { res = append(res, ar) } res = append(res, a) for _, s := range a.Songs { res = append(res, s) } } else if a, ok := musicObject.(data.Artist); ok { res = append(res, a) for _, al := range a.Albums { res = append(res, al) } } else { res = append(res, musicObject) } return res, nil } type SearchConfig struct { IgnoreErrors bool } func Search(search string, config SearchConfig) ([]data.MusicObject, error) { query, err := common.NewQuery(search) res := []data.MusicObject{} if err != nil && !config.IgnoreErrors { return res, err } for _, plugin := range namePlugins { s, err := plugin.Search(query) res = append(res, s...) if err != nil && !config.IgnoreErrors { return res, err } } return res, nil }