package decoder import ( "context" "fmt" "io" "strings" "github.com/jan/fm-rds-tx/internal/ingest" ) var ErrUnsupported = fmt.Errorf("decoder unsupported") type StreamMeta struct { ContentType string SampleRateHz int Channels int SourceID string } type Decoder interface { Name() string DecodeStream(ctx context.Context, r io.Reader, meta StreamMeta, emit func(ingest.PCMChunk) error) error } type Builder func() Decoder type Registry struct { byName map[string]Builder } func NewRegistry() *Registry { return &Registry{byName: map[string]Builder{}} } func (r *Registry) Register(name string, builder Builder) { if r == nil || builder == nil { return } r.byName[strings.ToLower(strings.TrimSpace(name))] = builder } func (r *Registry) Create(name string) (Decoder, error) { if r == nil { return nil, fmt.Errorf("%w: registry nil", ErrUnsupported) } builder, ok := r.byName[strings.ToLower(strings.TrimSpace(name))] if !ok { return nil, fmt.Errorf("%w: %s", ErrUnsupported, name) } return builder(), nil } func (r *Registry) SelectByContentType(contentType string) (Decoder, error) { ct := strings.ToLower(strings.TrimSpace(contentType)) switch { case strings.Contains(ct, "mpeg"), strings.Contains(ct, "mp3"): return r.Create("mp3") case strings.Contains(ct, "ogg"), strings.Contains(ct, "vorbis"): return r.Create("oggvorbis") case strings.Contains(ct, "aac"), strings.Contains(ct, "adts"): return r.Create("aac") default: return nil, fmt.Errorf("%w: content-type=%s", ErrUnsupported, contentType) } }