88 lines
2.0 KiB
Go
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
|
|
}
|