diff --git a/internal/downloader/client.go b/internal/downloader/client.go index 8dc4853..fa99515 100644 --- a/internal/downloader/client.go +++ b/internal/downloader/client.go @@ -12,12 +12,12 @@ import ( "time" "github.com/briandowns/spinner" - "github.com/mathismqn/godeez/internal/bpm" "github.com/mathismqn/godeez/internal/config" "github.com/mathismqn/godeez/internal/crypto" "github.com/mathismqn/godeez/internal/deezer" "github.com/mathismqn/godeez/internal/fileutil" "github.com/mathismqn/godeez/internal/logger" + "github.com/mathismqn/godeez/internal/provider" "github.com/mathismqn/godeez/internal/store" "github.com/mathismqn/godeez/internal/tags" ) @@ -192,19 +192,20 @@ func (c *Client) downloadSong(ctx context.Context, resource deezer.Resource, son return nil, SkipError{Path: path} } - var metricsChan chan *bpm.Metrics + var bpmChan chan provider.BPMKey var errChan chan error if opts.BPM { - metricsChan = make(chan *bpm.Metrics, 1) + bpmChan = make(chan provider.BPMKey, 1) errChan = make(chan error, 1) go func() { - metrics, err := bpm.FetchMetrics(ctx, c.deezerClient.Session.HttpClient, song.Artist, song.Title, song.Duration) + p := provider.BPMProvider{} + bpmKey, err := p.Fetch(ctx, c.deezerClient.Session.HttpClient, song.Artist, song.Title, song.Duration) if err != nil { errChan <- err return } - metricsChan <- metrics + bpmChan <- bpmKey }() } @@ -230,10 +231,10 @@ func (c *Client) downloadSong(ctx context.Context, resource deezer.Resource, son warnings = append(warnings, fmt.Sprintf("requested quality '%s' not available, using '%s' instead", opts.Quality, strings.ToLower(mediaFormat))) } - metrics := &bpm.Metrics{} + bpmKey := provider.BPMKey{} if opts.BPM { select { - case metrics = <-metricsChan: + case bpmKey = <-bpmChan: case err := <-errChan: if !errors.Is(err, context.Canceled) { warnings = append(warnings, fmt.Sprintf("failed to fetch BPM and key: %v", err)) @@ -246,7 +247,7 @@ func (c *Client) downloadSong(ctx context.Context, resource deezer.Resource, son warnings = append(warnings, fmt.Sprintf("failed to fetch cover image: %v", err)) } - warnings = append(warnings, c.finalizeDownload(resource, song, outputPath, mediaFormat, cover, metrics)...) + warnings = append(warnings, c.finalizeDownload(resource, song, outputPath, mediaFormat, cover, bpmKey)...) return warnings, nil } @@ -308,10 +309,10 @@ func (c *Client) streamToFile(ctx context.Context, stream io.ReadCloser, outputP return nil } -func (c *Client) finalizeDownload(resource deezer.Resource, song *deezer.Song, outputPath, mediaFormat string, cover []byte, metrics *bpm.Metrics) []string { +func (c *Client) finalizeDownload(resource deezer.Resource, song *deezer.Song, outputPath, mediaFormat string, cover []byte, bpmKey provider.BPMKey) []string { var warnings []string - if err := tags.AddTags(resource, song, cover, outputPath, metrics.BPM, metrics.Key); err != nil { + if err := tags.AddTags(resource, song, cover, outputPath, bpmKey.BPM, bpmKey.Key); err != nil { warnings = append(warnings, fmt.Sprintf("failed to add tags: %v", err)) } diff --git a/internal/bpm/bpm.go b/internal/provider/bpm.go similarity index 76% rename from internal/bpm/bpm.go rename to internal/provider/bpm.go index 400e568..abdcd64 100644 --- a/internal/bpm/bpm.go +++ b/internal/provider/bpm.go @@ -1,4 +1,4 @@ -package bpm +package provider import ( "bytes" @@ -6,7 +6,7 @@ import ( "fmt" "io" "net/http" - "net/url" + neturl "net/url" "regexp" "strconv" "strings" @@ -14,30 +14,32 @@ import ( "github.com/PuerkitoBio/goquery" ) -type Metrics struct { +type BPMProvider struct{} + +type BPMKey struct { BPM string Key string } -func FetchMetrics(ctx context.Context, httpClient *http.Client, artist, title, duration string) (*Metrics, error) { - url, err := findSongURL(ctx, httpClient, artist, title, duration) +func (p BPMProvider) Fetch(ctx context.Context, httpClient *http.Client, artist, title, duration string) (BPMKey, error) { + url, err := p.findSongURL(ctx, httpClient, artist, title, duration) if err != nil { - return nil, err + return BPMKey{}, err } - html, err := fetchPage(ctx, httpClient, url) + html, err := p.fetchPage(ctx, httpClient, url) if err != nil { - return nil, err + return BPMKey{}, err } - return parseMetrics(html) + return p.parse(html) } -func findSongURL(ctx context.Context, httpClient *http.Client, artist, title, duration string) (string, error) { +func (p BPMProvider) findSongURL(ctx context.Context, httpClient *http.Client, artist, title, duration string) (string, error) { rootUrl := "https://songbpm.com" reqUrl := rootUrl + "/searches" - values := url.Values{} + values := neturl.Values{} values.Add("query", fmt.Sprintf("%s %s", artist, title)) req, err := http.NewRequestWithContext(ctx, "POST", reqUrl, bytes.NewBufferString(values.Encode())) @@ -90,12 +92,13 @@ func findSongURL(ctx context.Context, httpClient *http.Client, artist, title, du } foundDuration := minutes*60 + seconds - duration, err := strconv.Atoi(duration) + wantDuration, err := strconv.Atoi(duration) if err != nil { return true } - if foundDuration <= (duration-2) || foundDuration >= (duration+2) { + const durationToleranceSec = 2 + if foundDuration <= (wantDuration-durationToleranceSec) || foundDuration >= (wantDuration+durationToleranceSec) { return true } @@ -112,7 +115,7 @@ func findSongURL(ctx context.Context, httpClient *http.Client, artist, title, du return rootUrl + url, nil } -func fetchPage(ctx context.Context, httpClient *http.Client, url string) (string, error) { +func (p BPMProvider) fetchPage(ctx context.Context, httpClient *http.Client, url string) (string, error) { req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return "", err @@ -136,7 +139,7 @@ func fetchPage(ctx context.Context, httpClient *http.Client, url string) (string return string(body), nil } -func parseMetrics(html string) (*Metrics, error) { +func (p BPMProvider) parse(html string) (BPMKey, error) { bpmRegex := regexp.MustCompile(`tempo of ]*>(\d+) BPM`) bpmMatch := bpmRegex.FindStringSubmatch(html) @@ -147,7 +150,7 @@ func parseMetrics(html string) (*Metrics, error) { modeMatch := modeRegex.FindStringSubmatch(html) if len(bpmMatch) != 2 || len(keyMatch) != 2 || len(modeMatch) != 2 { - return nil, fmt.Errorf("no data found") + return BPMKey{}, fmt.Errorf("no data found") } isMinor := false @@ -169,8 +172,9 @@ func parseMetrics(html string) (*Metrics, error) { key += "m" } - return &Metrics{ + return BPMKey{ BPM: bpm, Key: key, }, nil + }