Files
godeez/internal/download/metadata.go
T

88 lines
2.0 KiB
Go

package download
import (
"context"
"errors"
"fmt"
"net/http"
"github.com/mathismqn/godeez/internal/deezer"
)
type bpmKey struct {
BPM string
Key string
}
type metadataResult struct {
bpmKey bpmKey
genre string
warnings []string
}
// fetchMetadata looks up BPM, key and genre from third party sites, running
// the two lookups concurrently since neither depends on the other.
//
// Both channels are buffered so a goroutine whose result is never collected
// still exits instead of blocking forever. Failures become warnings rather
// than errors: these are nice to have tags, and a site being down should not
// cost the user the track. Cancellation is silent, because the run is already
// being torn down and a warning per track would just be noise.
func fetchMetadata(ctx context.Context, httpClient *http.Client, track *deezer.Track, opts Options) metadataResult {
if !opts.BPM && !opts.Genre {
return metadataResult{}
}
type bpmResult struct {
value bpmKey
err error
}
type genreResult struct {
value string
err error
}
bpmChan := make(chan bpmResult, 1)
genreChan := make(chan genreResult, 1)
if opts.BPM {
go func() {
result, err := fetchBPM(ctx, httpClient, track.Artist, track.Title, track.Duration)
bpmChan <- bpmResult{value: result, err: err}
}()
}
if opts.Genre {
go func() {
genre, err := fetchGenre(ctx, httpClient, track.Artist, track.FullTitle())
genreChan <- genreResult{value: genre, err: err}
}()
}
var result metadataResult
if opts.BPM {
r := <-bpmChan
if r.err != nil {
if !errors.Is(r.err, context.Canceled) {
result.warnings = append(result.warnings, fmt.Sprintf("failed to fetch BPM and key: %v", r.err))
}
} else {
result.bpmKey = r.value
}
}
if opts.Genre {
r := <-genreChan
if r.err != nil {
if !errors.Is(r.err, context.Canceled) {
result.warnings = append(result.warnings, fmt.Sprintf("failed to fetch genre: %v", r.err))
}
} else {
result.genre = r.value
}
}
return result
}