musiclink/internal/resolver/resolver.go

86 lines
1.9 KiB
Go

// Package resolver handles cross-service music link resolution.
package resolver
import (
"context"
"fmt"
"musiclink/internal/services"
)
// Resolver uses the idonthavespotify API to resolve music links.
type Resolver struct {
client *services.Client
}
// New creates a new Resolver.
func New(apiURL string) *Resolver {
return &Resolver{
client: services.NewClient(apiURL),
}
}
// Resolve takes a music link URL and returns equivalent links on other platforms.
func (r *Resolver) Resolve(ctx context.Context, link string) (*services.ResolvedLinks, error) {
resp, err := r.client.Resolve(ctx, link)
if err != nil {
return nil, fmt.Errorf("resolving link: %w", err)
}
return resp.ToResolvedLinks(), nil
}
// Format creates a formatted message from resolved links.
func Format(resolved *services.ResolvedLinks, title string) string {
var msg string
// Header with track info
if title != "" {
msg = fmt.Sprintf("%s\n\n", title)
}
// Service links in a consistent order
order := []services.ServiceType{
services.ServiceSpotify,
services.ServiceYouTube,
services.ServiceAppleMusic,
services.ServiceDeezer,
services.ServiceTidal,
services.ServiceSoundCloud,
services.ServiceBandcamp,
services.ServiceQobuz,
}
for _, svc := range order {
if url, ok := resolved.Links[svc]; ok {
msg += fmt.Sprintf("%s: %s\n", serviceName(svc), url)
}
}
return msg
}
// serviceName returns a human-readable name for a service.
func serviceName(svc services.ServiceType) string {
switch svc {
case services.ServiceSpotify:
return "Spotify"
case services.ServiceYouTube:
return "YouTube"
case services.ServiceAppleMusic:
return "Apple Music"
case services.ServiceDeezer:
return "Deezer"
case services.ServiceTidal:
return "Tidal"
case services.ServiceSoundCloud:
return "SoundCloud"
case services.ServiceBandcamp:
return "Bandcamp"
case services.ServiceQobuz:
return "Qobuz"
default:
return string(svc)
}
}