Files
metadata-agregator/internal/server/server.go
T
2026-05-07 14:27:25 +02:00

246 lines
5.8 KiB
Go

package server
import (
"context"
"errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/metadata-agregator/internal/provider/musicbrainz"
"github.com/metadata-agregator/internal/repository"
"github.com/metadata-agregator/internal/service"
metadatav1 "github.com/metadata-agregator/pkg/gen/metadata/v1"
)
type MetadataServer struct {
metadatav1.UnimplementedMetadataServiceServer
services map[metadatav1.Provider]*service.MetadataService
}
func NewMetadataServer(services map[metadatav1.Provider]*service.MetadataService) *MetadataServer {
return &MetadataServer{services: services}
}
func (s *MetadataServer) getService(p metadatav1.Provider) (*service.MetadataService, error) {
if p == metadatav1.Provider_PROVIDER_UNSPECIFIED {
p = metadatav1.Provider_PROVIDER_MUSICBRAINZ
}
svc, ok := s.services[p]
if !ok {
return nil, status.Errorf(codes.InvalidArgument, "unknown provider: %v", p)
}
return svc, nil
}
func (s *MetadataServer) GetArtist(ctx context.Context, req *metadatav1.GetArtistRequest) (*metadatav1.Artist, error) {
svc, err := s.getService(req.Provider)
if err != nil {
return nil, err
}
var id string
switch v := req.Identifier.(type) {
case *metadatav1.GetArtistRequest_Id:
id = v.Id
case *metadatav1.GetArtistRequest_External:
id = v.External.SourceId
default:
return nil, status.Error(codes.InvalidArgument, "identifier required")
}
artist, err := svc.GetArtist(ctx, id)
if err != nil {
return nil, toGRPCError(err)
}
return toProtoArtist(artist), nil
}
func (s *MetadataServer) SearchArtists(ctx context.Context, req *metadatav1.SearchArtistsRequest) (*metadatav1.SearchArtistsResponse, error) {
svc, err := s.getService(req.Provider)
if err != nil {
return nil, err
}
limit := int(req.Limit)
if limit <= 0 {
limit = 25
}
result, err := svc.SearchArtists(ctx, req.Query, limit, int(req.Offset))
if err != nil {
return nil, toGRPCError(err)
}
resp := &metadatav1.SearchArtistsResponse{
Total: int32(result.Total),
}
for _, a := range result.Items {
resp.Artists = append(resp.Artists, toProtoArtist(&a))
}
return resp, nil
}
func (s *MetadataServer) SearchAlbums(ctx context.Context, req *metadatav1.SearchAlbumsRequest) (*metadatav1.SearchAlbumsResponse, error) {
svc, err := s.getService(req.Provider)
if err != nil {
return nil, err
}
limit := int(req.Limit)
if limit <= 0 {
limit = 25
}
result, err := svc.SearchAlbums(ctx, req.Query, req.Artist, limit, int(req.Offset))
if err != nil {
return nil, toGRPCError(err)
}
resp := &metadatav1.SearchAlbumsResponse{
Total: int32(result.Total),
}
for _, a := range result.Items {
resp.Albums = append(resp.Albums, toProtoAlbum(&a))
}
return resp, nil
}
func (s *MetadataServer) GetAlbum(ctx context.Context, req *metadatav1.GetAlbumRequest) (*metadatav1.Album, error) {
svc, err := s.getService(req.Provider)
if err != nil {
return nil, err
}
var id string
switch v := req.Identifier.(type) {
case *metadatav1.GetAlbumRequest_Id:
id = v.Id
case *metadatav1.GetAlbumRequest_External:
id = v.External.SourceId
default:
return nil, status.Error(codes.InvalidArgument, "identifier required")
}
album, err := svc.GetAlbum(ctx, id)
if err != nil {
return nil, toGRPCError(err)
}
return toProtoAlbum(album), nil
}
func (s *MetadataServer) GetArtistAlbums(ctx context.Context, req *metadatav1.GetArtistAlbumsRequest) (*metadatav1.GetArtistAlbumsResponse, error) {
svc, err := s.getService(req.Provider)
if err != nil {
return nil, err
}
limit := int(req.Limit)
if limit <= 0 {
limit = 25
}
result, err := svc.GetArtistAlbums(ctx, req.ArtistId, limit, int(req.Offset))
if err != nil {
return nil, toGRPCError(err)
}
resp := &metadatav1.GetArtistAlbumsResponse{
Total: int32(result.Total),
}
for _, a := range result.Items {
resp.Albums = append(resp.Albums, toProtoAlbum(&a))
}
return resp, nil
}
func (s *MetadataServer) GetTrack(ctx context.Context, req *metadatav1.GetTrackRequest) (*metadatav1.Track, error) {
svc, err := s.getService(req.Provider)
if err != nil {
return nil, err
}
var track *metadatav1.Track
switch v := req.Identifier.(type) {
case *metadatav1.GetTrackRequest_Id:
t, err := svc.GetTrack(ctx, v.Id)
if err != nil {
return nil, toGRPCError(err)
}
track = toProtoTrack(t)
case *metadatav1.GetTrackRequest_External:
t, err := svc.GetTrack(ctx, v.External.SourceId)
if err != nil {
return nil, toGRPCError(err)
}
track = toProtoTrack(t)
case *metadatav1.GetTrackRequest_Isrc:
t, err := svc.GetTrackByISRC(ctx, v.Isrc)
if err != nil {
return nil, toGRPCError(err)
}
track = toProtoTrack(t)
default:
return nil, status.Error(codes.InvalidArgument, "identifier required")
}
return track, nil
}
func (s *MetadataServer) GetAlbumTracks(ctx context.Context, req *metadatav1.GetAlbumTracksRequest) (*metadatav1.GetAlbumTracksResponse, error) {
svc, err := s.getService(req.Provider)
if err != nil {
return nil, err
}
tracks, err := svc.GetAlbumTracks(ctx, req.AlbumId)
if err != nil {
return nil, toGRPCError(err)
}
resp := &metadatav1.GetAlbumTracksResponse{}
for _, t := range tracks {
resp.Tracks = append(resp.Tracks, toProtoTrack(&t))
}
return resp, nil
}
func (s *MetadataServer) SyncArtist(ctx context.Context, req *metadatav1.SyncArtistRequest) (*metadatav1.SyncArtistResponse, error) {
return nil, status.Error(codes.Unimplemented, "sync not yet implemented")
}
func toGRPCError(err error) error {
if err == nil {
return nil
}
if errors.Is(err, repository.ErrNotFound) {
return status.Error(codes.NotFound, "not found")
}
if errors.Is(err, musicbrainz.ErrNotFound) {
return status.Error(codes.NotFound, "not found")
}
if errors.Is(err, musicbrainz.ErrRateLimited) {
return status.Error(codes.ResourceExhausted, "rate limited")
}
return status.Errorf(codes.Internal, "internal error: %v", err)
}