feat(server/m7-cover-sources): MBCAA provider wrapper
Adapts the existing Fetcher-based MBCAA client to the new Provider abstraction. mbcaaProvider embeds *Fetcher and implements Provider + AlbumCoverProvider + TestableProvider. init() registers a default-config instance; NewMBCAAProviderFromConfig swaps in production config (User-Agent, MinPeriod) at boot. Wrapper approach (rather than reimplementing the HTTP logic) keeps the package compiling during the T4–T8 window: enricher.go and main.go still reference the legacy Fetcher type / NewFetcher constructor / Fetch method, all of which remain unchanged in fetcher.go. A-T8 will consolidate the HTTP logic into this file and delete fetcher.go after the enricher rewrite removes the last legacy call sites. TestConnection hits a hardcoded popular release MBID (Beatles "Abbey Road" UK release) and treats both 200 and 404 as "connection works" — MBCAA needs no auth, so only 5xx / network failures surface. Tests cover ID/DisplayName/Capability metadata, success path, 404→ErrNotFound, disabled-returns-ErrNotFound, Configure toggle, TestConnection variants, and NewMBCAAProviderFromConfig swap.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package coverart
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// mbcaaProvider implements Provider, AlbumCoverProvider, and
|
||||
// TestableProvider. It wraps an *Fetcher (the existing
|
||||
// MBCAA-specific HTTP client) for the actual fetch logic, adapting
|
||||
// the Fetcher's Fetch method to the AlbumCoverProvider interface.
|
||||
//
|
||||
// The wrapping (rather than reimplementing) is temporary: A-T8 will
|
||||
// consolidate fetcher.go's logic into this file and delete fetcher.go.
|
||||
// During the T4–T8 window, this struct lets the new provider chain
|
||||
// coexist with the legacy Fetcher used by enricher.go and main.go.
|
||||
type mbcaaProvider struct {
|
||||
fetcher *Fetcher
|
||||
enabled atomic.Bool
|
||||
}
|
||||
|
||||
// Sample MBID used by TestConnection. The Beatles' "Abbey Road" UK
|
||||
// release; verified present on MBCAA at design time. Both 200 (image
|
||||
// returned) and 404 (no art for the sample) are treated as "connection
|
||||
// works" since MBCAA needs no auth — only network failures surface.
|
||||
const mbcaaTestSampleMBID = "f4b3df80-8a6c-3e87-b51e-5c1ee70a78ee"
|
||||
|
||||
func init() {
|
||||
// Register with a default-config Fetcher. main.go (in A-T8) will
|
||||
// call NewMBCAAProviderFromConfig to swap in the production
|
||||
// User-Agent + MinPeriod. Default config values come from
|
||||
// FetcherConfig's zero-value handling in NewFetcher.
|
||||
Register(&mbcaaProvider{
|
||||
fetcher: NewFetcher(FetcherConfig{}),
|
||||
})
|
||||
}
|
||||
|
||||
// NewMBCAAProviderFromConfig replaces the registered MBCAA provider's
|
||||
// internal Fetcher with one built from the supplied config. main.go
|
||||
// calls this at boot to set the production User-Agent. Idempotent;
|
||||
// safe to call repeatedly.
|
||||
func NewMBCAAProviderFromConfig(cfg FetcherConfig) {
|
||||
p, err := ProviderByID("mbcaa")
|
||||
if err != nil {
|
||||
// init() should have registered it; if not, register fresh.
|
||||
Register(&mbcaaProvider{fetcher: NewFetcher(cfg)})
|
||||
return
|
||||
}
|
||||
mp, ok := p.(*mbcaaProvider)
|
||||
if !ok {
|
||||
// Some other provider claimed the "mbcaa" ID — registry bug.
|
||||
panic("coverart: provider with ID 'mbcaa' is not *mbcaaProvider")
|
||||
}
|
||||
mp.fetcher = NewFetcher(cfg)
|
||||
}
|
||||
|
||||
func (p *mbcaaProvider) ID() string { return "mbcaa" }
|
||||
func (p *mbcaaProvider) DisplayName() string { return "Cover Art Archive" }
|
||||
func (p *mbcaaProvider) RequiresAPIKey() bool { return false }
|
||||
func (p *mbcaaProvider) DefaultEnabled() bool { return true }
|
||||
|
||||
func (p *mbcaaProvider) Configure(s ProviderSettings) error {
|
||||
p.enabled.Store(s.Enabled)
|
||||
return nil // MBCAA has no per-operator config beyond enabled
|
||||
}
|
||||
|
||||
// FetchAlbumCover delegates to the wrapped Fetcher. The Fetcher's
|
||||
// existing Fetch method already returns ErrNotFound / ErrTransient
|
||||
// per the contract, so no error mapping is needed.
|
||||
func (p *mbcaaProvider) FetchAlbumCover(ctx context.Context, mbid string) ([]byte, error) {
|
||||
if !p.enabled.Load() {
|
||||
return nil, ErrNotFound // defensive; enricher already filters on enabled
|
||||
}
|
||||
return p.fetcher.Fetch(ctx, mbid)
|
||||
}
|
||||
|
||||
// TestConnection hits the sample MBID's front-500 endpoint. A 200
|
||||
// (image bytes) or 404 (no art for sample) both indicate the
|
||||
// connection works; 5xx / network errors surface as TestConnection
|
||||
// failures.
|
||||
func (p *mbcaaProvider) TestConnection(ctx context.Context) error {
|
||||
_, err := p.fetcher.Fetch(ctx, mbcaaTestSampleMBID)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user