package grpc import ( "context" "errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ttsv1 "gitea.nik4nao.com/nik/home-services/gen/tts/v1" "gitea.nik4nao.com/nik/home-services/tts-gateway/internal/app" "gitea.nik4nao.com/nik/home-services/tts-gateway/internal/core/domain" "gitea.nik4nao.com/nik/home-services/tts-gateway/internal/core/ports/driving" ) type TTSGRPC struct { ttsv1.UnimplementedTTSServiceServer svc driving.TTSService } // NewTTSGRPC constructs the gRPC adapter for TTSService. func NewTTSGRPC(svc driving.TTSService) *TTSGRPC { return &TTSGRPC{svc: svc} } // Synthesize translates a protobuf request into a domain call, applying // uma-tts-api's sampling defaults for any knob the client omits. func (h *TTSGRPC) Synthesize(ctx context.Context, req *ttsv1.SynthesizeRequest) (*ttsv1.SynthesizeResponse, error) { params := domain.SynthesisParams{ NoiseScale: domain.DefaultNoiseScale, NoiseScaleW: domain.DefaultNoiseScaleW, LengthScale: domain.DefaultLengthScale, } if req.NoiseScale != nil { params.NoiseScale = *req.NoiseScale } if req.NoiseScaleW != nil { params.NoiseScaleW = *req.NoiseScaleW } if req.LengthScale != nil { params.LengthScale = *req.LengthScale } clip, err := h.svc.Synthesize(ctx, req.SpeakerName, req.Text, params) if err != nil { return nil, grpcError(err) } return &ttsv1.SynthesizeResponse{Audio: clip.Data, MimeType: clip.MimeType}, nil } // ListSpeakers returns the (optionally filtered) speaker roster. func (h *TTSGRPC) ListSpeakers(ctx context.Context, req *ttsv1.ListSpeakersRequest) (*ttsv1.ListSpeakersResponse, error) { names := h.svc.ListSpeakers(ctx, req.Search) return &ttsv1.ListSpeakersResponse{SpeakerNames: names}, nil } // grpcError maps domain errors to appropriate gRPC status codes, mirroring // uma-tts-api's /synthesize 400 response for an unknown speaker name. func grpcError(err error) error { if errors.Is(err, app.ErrSpeakerNotFound) { return status.Errorf(codes.InvalidArgument, "%v", err) } return status.Errorf(codes.Internal, "%v", err) }