Files
godeez/internal/provider/genre.go
T

149 lines
3.5 KiB
Go

package provider
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
)
var electronicKeywords = []string{
"Ambient", "Bass", "Big Room", "Breakbeat", "Dance", "Disco", "Downtempo",
"Drum And Bass", "Dub", "Dubstep", "EDM", "Electro", "Electronic", "Electronica",
"Eurodance", "Gabber", "Garage", "Hardcore", "Hardstyle", "House", "Industrial",
"Jungle", "Moombahton", "Synthpop", "Synthwave", "Techno", "Trance", "Trap",
"Trip Hop", "Vaporwave",
}
var nonElectronicKeywords = []string{
"Blues", "Chillout", "Classical", "Country", "Folk", "Funk", "Hip Hop", "Jazz",
"Latin", "Metal", "Pop", "R&B", "Rap", "Reggae", "Rock", "Soul",
}
type GenreProvider struct{}
func (p GenreProvider) Fetch(ctx context.Context, httpClient *http.Client, artist, title string) (string, error) {
reqUrl := fmt.Sprintf("https://www.last.fm/music/%s/%s/+tags", artist, title)
doc, err := p.fetchPage(ctx, httpClient, reqUrl)
if err != nil {
return "", err
}
tags := p.parse(doc)
if len(tags) == 0 {
return "", fmt.Errorf("no data found")
}
if len(tags) > 2 {
tags = tags[:2]
}
filteredTags := p.filterTags(tags)
if len(filteredTags) == 0 {
return "", fmt.Errorf("no data found")
}
genre := p.formatTags(filteredTags)
return genre, nil
}
func (p GenreProvider) fetchPage(ctx context.Context, httpClient *http.Client, reqUrl string) (*goquery.Document, error) {
req, err := http.NewRequestWithContext(ctx, "GET", reqUrl, nil)
if err != nil {
return nil, err
}
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, err
}
return doc, nil
}
func (p GenreProvider) parse(doc *goquery.Document) []string {
var tags []string
doc.Find("ol.big-tags .big-tags-item-name a").Each(func(_ int, s *goquery.Selection) {
tag := strings.TrimSpace(s.Text())
if tag != "" {
tags = append(tags, tag)
}
})
return tags
}
func (p GenreProvider) filterTags(tags []string) []string {
var electronicTags []string
var nonElectronicTags []string
for _, tag := range tags {
if p.isElectronicGenre(tag) {
electronicTags = append(electronicTags, tag)
} else if p.isNonElectronicGenre(tag) {
nonElectronicTags = append(nonElectronicTags, tag)
}
}
var filteredTags []string
filteredTags = append(filteredTags, electronicTags...)
if len(electronicTags) > 0 {
filteredTags = append(filteredTags, nonElectronicTags...)
}
return filteredTags
}
func (p GenreProvider) isElectronicGenre(tag string) bool {
tagLower := strings.ToLower(tag)
for _, allowed := range electronicKeywords {
if strings.Contains(tagLower, strings.ToLower(allowed)) {
return true
}
}
return false
}
func (p GenreProvider) isNonElectronicGenre(tag string) bool {
tagLower := strings.ToLower(tag)
for _, allowed := range nonElectronicKeywords {
if strings.Contains(tagLower, strings.ToLower(allowed)) {
return true
}
}
return false
}
func (p GenreProvider) formatTags(tags []string) string {
var formatted []string
for _, tag := range tags {
tag = strings.TrimSpace(tag)
if tag == "" {
continue
}
words := strings.Fields(tag)
for i, w := range words {
if len(w) > 0 {
words[i] = strings.ToUpper(string(w[0])) + strings.ToLower(w[1:])
}
}
formatted = append(formatted, strings.Join(words, " "))
}
return strings.Join(formatted, " / ")
}