Compare commits

...

3 Commits

Author SHA1 Message Date
Alexander fb2e4b9107 Add SearchAlbum endpoint 2026-05-07 14:27:25 +02:00
Alexander de52fcbc79 fix: fail on database connection error when database is configured 2026-05-07 14:27:25 +02:00
Alexander de674376ed feat: initial implementation of metadata aggregator
- gRPC service with MusicBrainz provider
- PostgreSQL schema with migrations
- Service layer with database-first caching
- Repository pattern for data access
- YAML configuration support
- Research documentation for 17 music metadata projects
2026-05-07 14:27:25 +02:00
13 changed files with 1054 additions and 97 deletions
+2
View File
@@ -5,8 +5,10 @@ server
*.test
*.out
.env
config.yaml
*.log
vendor/
.pre-commit-config.yaml
docs/research/*/repo/
docs/research/*/repo-index/
+104
View File
@@ -0,0 +1,104 @@
# Metadata Aggregator
gRPC service for aggregating music metadata from multiple providers with PostgreSQL caching.
## Features
- **Multi-provider support** - Currently MusicBrainz, extensible for Spotify, Discogs, etc.
- **Database-first caching** - Check local DB before hitting external APIs
- **Rate limiting** - Respects provider API limits (1 req/sec for MusicBrainz)
- **Provider-only mode** - Works without database for quick testing
## Quick Start
```bash
# Build
nix build .#server
# Run (no database)
./result/bin/server
# Run (with database)
./result/bin/server -config config.yaml
```
## Configuration
```yaml
server:
port: 50051
database:
host: localhost
port: 5432
user: metadata
password: metadata
name: metadata
sslmode: disable
```
Or via environment: `DATABASE_URL=postgres://user:pass@host:5432/db`
## Database
```bash
cd database
docker compose up -d
```
PostgreSQL 16 with `pg_prewarm` for automatic cache warming.
## API
```bash
# Search artists
grpcurl -plaintext -d '{"query": "Radiohead"}' localhost:50051 metadata.v1.MetadataService/SearchArtists
# Get artist
grpcurl -plaintext -d '{"id": "a74b1b7f-71a5-4011-9441-d0b5e4122711"}' localhost:50051 metadata.v1.MetadataService/GetArtist
# Get album
grpcurl -plaintext -d '{"id": "b1392450-e666-3926-a536-22c65f834433"}' localhost:50051 metadata.v1.MetadataService/GetAlbum
# Get album tracks
grpcurl -plaintext -d '{"album_id": "b1392450-e666-3926-a536-22c65f834433"}' localhost:50051 metadata.v1.MetadataService/GetAlbumTracks
```
## Project Structure
```
├── cmd/server/ # Server entrypoint
├── internal/
│ ├── config/ # YAML configuration
│ ├── domain/ # Domain types
│ ├── provider/ # External API clients
│ │ └── musicbrainz/ # MusicBrainz implementation
│ ├── repository/ # Database access
│ │ └── postgres/ # PostgreSQL implementation
│ ├── server/ # gRPC handlers
│ └── service/ # Business logic (DB-first caching)
├── pkg/gen/ # Generated protobuf code
├── proto/ # Proto definitions
├── database/ # Docker compose + migrations
└── tests/e2e/ # End-to-end tests
```
## Development
```bash
# Enter dev shell
nix develop
# Generate protobuf
buf generate
# Run tests
go test ./...
# Run e2e tests (requires network)
go test -v ./tests/e2e/...
```
## License
MIT
+130
View File
@@ -0,0 +1,130 @@
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"os"
"os/signal"
"syscall"
"time"
"github.com/jackc/pgx/v5/pgxpool"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"github.com/metadata-agregator/internal/config"
"github.com/metadata-agregator/internal/provider/musicbrainz"
"github.com/metadata-agregator/internal/repository/postgres"
"github.com/metadata-agregator/internal/server"
"github.com/metadata-agregator/internal/service"
metadatav1 "github.com/metadata-agregator/pkg/gen/metadata/v1"
)
func main() {
configPath := flag.String("config", "", "path to config file")
flag.Parse()
cfg, err := config.Load(*configPath)
if err != nil {
log.Fatalf("failed to load config: %v", err)
}
ctx := context.Background()
services, cleanup := buildServices(ctx, cfg)
defer cleanup()
addr := fmt.Sprintf(":%d", cfg.Server.Port)
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
metadatav1.RegisterMetadataServiceServer(grpcServer, server.NewMetadataServer(services))
reflection.Register(grpcServer)
go gracefulShutdown(grpcServer)
log.Printf("gRPC server listening on %s", addr)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func buildServices(ctx context.Context, cfg *config.Config) (map[metadatav1.Provider]*service.MetadataService, func()) {
mb := musicbrainz.New()
services := make(map[metadatav1.Provider]*service.MetadataService)
dbURL := cfg.Database.DSN()
if dbURL == "" {
dbURL = os.Getenv("DATABASE_URL")
}
if dbURL == "" {
log.Println("no database configured, running in provider-only mode")
services[metadatav1.Provider_PROVIDER_MUSICBRAINZ] = service.NewMetadataService(
&noopArtistRepo{},
&noopAlbumRepo{},
&noopTrackRepo{},
mb,
)
return services, func() {}
}
pool, err := connectDB(ctx, dbURL)
if err != nil {
log.Fatalf("database connection failed: %v", err)
}
artistRepo := postgres.NewArtistRepository(pool)
albumRepo := postgres.NewAlbumRepository(pool)
trackRepo := postgres.NewTrackRepository(pool)
services[metadatav1.Provider_PROVIDER_MUSICBRAINZ] = service.NewMetadataService(
artistRepo,
albumRepo,
trackRepo,
mb,
)
log.Println("database connected, caching enabled")
return services, func() { pool.Close() }
}
func connectDB(ctx context.Context, dbURL string) (*pgxpool.Pool, error) {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
config, err := pgxpool.ParseConfig(dbURL)
if err != nil {
return nil, err
}
config.MaxConns = 10
config.MinConns = 2
pool, err := pgxpool.NewWithConfig(ctx, config)
if err != nil {
return nil, err
}
if err := pool.Ping(ctx); err != nil {
pool.Close()
return nil, err
}
return pool, nil
}
func gracefulShutdown(server *grpc.Server) {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
log.Println("shutting down...")
server.GracefulStop()
}
+66
View File
@@ -0,0 +1,66 @@
package main
import (
"context"
"github.com/metadata-agregator/internal/domain"
"github.com/metadata-agregator/internal/repository"
)
type noopArtistRepo struct{}
func (r *noopArtistRepo) GetByID(ctx context.Context, id string) (*domain.Artist, error) {
return nil, repository.ErrNotFound
}
func (r *noopArtistRepo) GetByExternalID(ctx context.Context, source, sourceID string) (*domain.Artist, error) {
return nil, repository.ErrNotFound
}
func (r *noopArtistRepo) Search(ctx context.Context, query string, limit, offset int) (*domain.SearchResult[domain.Artist], error) {
return &domain.SearchResult[domain.Artist]{}, nil
}
func (r *noopArtistRepo) Save(ctx context.Context, artist *domain.Artist) error {
return nil
}
type noopAlbumRepo struct{}
func (r *noopAlbumRepo) GetByID(ctx context.Context, id string) (*domain.Album, error) {
return nil, repository.ErrNotFound
}
func (r *noopAlbumRepo) GetByExternalID(ctx context.Context, source, sourceID string) (*domain.Album, error) {
return nil, repository.ErrNotFound
}
func (r *noopAlbumRepo) GetByArtistID(ctx context.Context, artistID string, limit, offset int) (*domain.SearchResult[domain.Album], error) {
return &domain.SearchResult[domain.Album]{}, nil
}
func (r *noopAlbumRepo) Save(ctx context.Context, album *domain.Album) error {
return nil
}
type noopTrackRepo struct{}
func (r *noopTrackRepo) GetByID(ctx context.Context, id string) (*domain.Track, error) {
return nil, repository.ErrNotFound
}
func (r *noopTrackRepo) GetByExternalID(ctx context.Context, source, sourceID string) (*domain.Track, error) {
return nil, repository.ErrNotFound
}
func (r *noopTrackRepo) GetByISRC(ctx context.Context, isrc string) (*domain.Track, error) {
return nil, repository.ErrNotFound
}
func (r *noopTrackRepo) GetByAlbumID(ctx context.Context, albumID string) ([]domain.Track, error) {
return nil, nil
}
func (r *noopTrackRepo) Save(ctx context.Context, track *domain.Track) error {
return nil
}
+1
View File
@@ -1,3 +1,4 @@
listen_addresses = '*'
shared_preload_libraries = 'pg_prewarm'
pg_prewarm.autoprewarm = true
+43
View File
@@ -91,6 +91,49 @@ func (p *Provider) GetAlbum(ctx context.Context, id string) (*domain.Album, erro
return mapAlbum(mb, release), nil
}
func (p *Provider) SearchAlbums(ctx context.Context, query string, artist string, limit, offset int) (*domain.SearchResult[domain.Album], error) {
if limit <= 0 || limit > 100 {
limit = 25
}
var luceneQuery string
if artist != "" && query != "" {
luceneQuery = fmt.Sprintf("releasegroup:%s AND artist:%s", escapeQuery(query), escapeQuery(artist))
} else if artist != "" {
luceneQuery = fmt.Sprintf("artist:%s", escapeQuery(artist))
} else {
luceneQuery = fmt.Sprintf("releasegroup:%s", escapeQuery(query))
}
data, err := p.client.search(ctx, "release-group", luceneQuery, limit, offset)
if err != nil {
return nil, fmt.Errorf("search albums: %w", err)
}
var resp struct {
Count int `json:"count"`
Offset int `json:"offset"`
ReleaseGroups []*mbReleaseGroup `json:"release-groups"`
}
if err := decodeInto(data, &resp); err != nil {
return nil, err
}
result := &domain.SearchResult[domain.Album]{
Total: resp.Count,
Limit: limit,
Offset: offset,
}
for _, mb := range resp.ReleaseGroups {
if album := mapAlbum(mb, nil); album != nil {
result.Items = append(result.Items, *album)
}
}
return result, nil
}
func (p *Provider) GetArtistAlbums(ctx context.Context, artistID string, limit, offset int) (*domain.SearchResult[domain.Album], error) {
if limit <= 0 || limit > 100 {
limit = 25
+1
View File
@@ -13,6 +13,7 @@ type Provider interface {
SearchArtists(ctx context.Context, query string, limit, offset int) (*domain.SearchResult[domain.Artist], error)
GetAlbum(ctx context.Context, id string) (*domain.Album, error)
SearchAlbums(ctx context.Context, query string, artist string, limit, offset int) (*domain.SearchResult[domain.Album], error)
GetArtistAlbums(ctx context.Context, artistID string, limit, offset int) (*domain.SearchResult[domain.Album], error)
GetTrack(ctx context.Context, id string) (*domain.Track, error)
+161
View File
@@ -0,0 +1,161 @@
package server
import (
"github.com/metadata-agregator/internal/domain"
metadatav1 "github.com/metadata-agregator/pkg/gen/metadata/v1"
)
func toProtoArtist(d *domain.Artist) *metadatav1.Artist {
if d == nil {
return nil
}
a := &metadatav1.Artist{
Id: d.ID,
Name: d.Name,
SortName: d.SortName,
ArtistType: d.Type,
Country: d.Country,
Description: d.Description,
ImageUrl: d.ImageURL,
}
if d.FormedDate != nil {
a.FormedDate = d.FormedDate.Format("2006-01-02")
}
if d.DisbandedDate != nil {
a.DisbandedDate = d.DisbandedDate.Format("2006-01-02")
}
for _, g := range d.Genres {
a.Genres = append(a.Genres, &metadatav1.Genre{
Id: g.ID,
Name: g.Name,
})
}
for _, e := range d.ExternalIDs {
a.ExternalIds = append(a.ExternalIds, &metadatav1.ExternalID{
Source: e.Source,
SourceId: e.SourceID,
Url: e.URL,
})
}
return a
}
func toProtoAlbum(d *domain.Album) *metadatav1.Album {
if d == nil {
return nil
}
a := &metadatav1.Album{
Id: d.ID,
Title: d.Title,
AlbumType: d.Type,
Upc: d.UPC,
TotalTracks: int32(d.TotalTracks),
TotalDiscs: int32(d.TotalDiscs),
CoverUrl: d.CoverURL,
}
if d.ReleaseDate != nil {
a.ReleaseDate = d.ReleaseDate.Format("2006-01-02")
}
for _, ac := range d.Artists {
a.Artists = append(a.Artists, toProtoArtistCredit(&ac))
}
if d.Label != nil {
a.Label = &metadatav1.Label{
Id: d.Label.ID,
Name: d.Label.Name,
Country: d.Label.Country,
}
}
for _, g := range d.Genres {
a.Genres = append(a.Genres, &metadatav1.Genre{
Id: g.ID,
Name: g.Name,
})
}
for _, e := range d.ExternalIDs {
a.ExternalIds = append(a.ExternalIds, &metadatav1.ExternalID{
Source: e.Source,
SourceId: e.SourceID,
Url: e.URL,
})
}
return a
}
func toProtoTrack(d *domain.Track) *metadatav1.Track {
if d == nil {
return nil
}
t := &metadatav1.Track{
Id: d.ID,
Title: d.Title,
DurationMs: int32(d.DurationMs),
Isrc: d.ISRC,
Explicit: d.Explicit,
DiscNumber: int32(d.DiscNumber),
TrackNumber: int32(d.TrackNumber),
}
for _, ac := range d.Artists {
t.Artists = append(t.Artists, toProtoArtistCredit(&ac))
}
if d.Work != nil {
t.Work = toProtoWork(d.Work)
}
for _, e := range d.ExternalIDs {
t.ExternalIds = append(t.ExternalIds, &metadatav1.ExternalID{
Source: e.Source,
SourceId: e.SourceID,
Url: e.URL,
})
}
return t
}
func toProtoWork(d *domain.Work) *metadatav1.Work {
if d == nil {
return nil
}
w := &metadatav1.Work{
Id: d.ID,
Title: d.Title,
WorkType: d.Type,
Language: d.Language,
}
for _, c := range d.Composers {
w.Composers = append(w.Composers, toProtoArtistCredit(&c))
}
return w
}
func toProtoArtistCredit(d *domain.ArtistCredit) *metadatav1.ArtistCredit {
if d == nil {
return nil
}
return &metadatav1.ArtistCredit{
Artist: toProtoArtist(&d.Artist),
Role: d.Role,
Position: int32(d.Position),
JoinPhrase: d.JoinPhrase,
}
}
+245
View File
@@ -0,0 +1,245 @@
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)
}
+4
View File
@@ -60,6 +60,10 @@ func (s *MetadataService) SearchArtists(ctx context.Context, query string, limit
return s.provider.SearchArtists(ctx, query, limit, offset)
}
func (s *MetadataService) SearchAlbums(ctx context.Context, query string, artist string, limit, offset int) (*domain.SearchResult[domain.Album], error) {
return s.provider.SearchAlbums(ctx, query, artist, limit, offset)
}
func (s *MetadataService) GetAlbum(ctx context.Context, id string) (*domain.Album, error) {
album, err := s.albums.GetByExternalID(ctx, s.provider.Name(), id)
if err == nil {
+241 -97
View File
@@ -541,6 +541,134 @@ func (x *GetAlbumTracksRequest) GetProvider() Provider {
return Provider_PROVIDER_UNSPECIFIED
}
type SearchAlbumsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Query string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
Artist string `protobuf:"bytes,2,opt,name=artist,proto3" json:"artist,omitempty"`
Limit int32 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"`
Offset int32 `protobuf:"varint,4,opt,name=offset,proto3" json:"offset,omitempty"`
Provider Provider `protobuf:"varint,5,opt,name=provider,proto3,enum=metadata.v1.Provider" json:"provider,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SearchAlbumsRequest) Reset() {
*x = SearchAlbumsRequest{}
mi := &file_metadata_v1_metadata_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SearchAlbumsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SearchAlbumsRequest) ProtoMessage() {}
func (x *SearchAlbumsRequest) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SearchAlbumsRequest.ProtoReflect.Descriptor instead.
func (*SearchAlbumsRequest) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{6}
}
func (x *SearchAlbumsRequest) GetQuery() string {
if x != nil {
return x.Query
}
return ""
}
func (x *SearchAlbumsRequest) GetArtist() string {
if x != nil {
return x.Artist
}
return ""
}
func (x *SearchAlbumsRequest) GetLimit() int32 {
if x != nil {
return x.Limit
}
return 0
}
func (x *SearchAlbumsRequest) GetOffset() int32 {
if x != nil {
return x.Offset
}
return 0
}
func (x *SearchAlbumsRequest) GetProvider() Provider {
if x != nil {
return x.Provider
}
return Provider_PROVIDER_UNSPECIFIED
}
type SearchAlbumsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Albums []*Album `protobuf:"bytes,1,rep,name=albums,proto3" json:"albums,omitempty"`
Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SearchAlbumsResponse) Reset() {
*x = SearchAlbumsResponse{}
mi := &file_metadata_v1_metadata_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SearchAlbumsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SearchAlbumsResponse) ProtoMessage() {}
func (x *SearchAlbumsResponse) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SearchAlbumsResponse.ProtoReflect.Descriptor instead.
func (*SearchAlbumsResponse) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{7}
}
func (x *SearchAlbumsResponse) GetAlbums() []*Album {
if x != nil {
return x.Albums
}
return nil
}
func (x *SearchAlbumsResponse) GetTotal() int32 {
if x != nil {
return x.Total
}
return 0
}
type SyncArtistRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Types that are valid to be assigned to Target:
@@ -555,7 +683,7 @@ type SyncArtistRequest struct {
func (x *SyncArtistRequest) Reset() {
*x = SyncArtistRequest{}
mi := &file_metadata_v1_metadata_proto_msgTypes[6]
mi := &file_metadata_v1_metadata_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -567,7 +695,7 @@ func (x *SyncArtistRequest) String() string {
func (*SyncArtistRequest) ProtoMessage() {}
func (x *SyncArtistRequest) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[6]
mi := &file_metadata_v1_metadata_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -580,7 +708,7 @@ func (x *SyncArtistRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use SyncArtistRequest.ProtoReflect.Descriptor instead.
func (*SyncArtistRequest) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{6}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{8}
}
func (x *SyncArtistRequest) GetTarget() isSyncArtistRequest_Target {
@@ -641,7 +769,7 @@ type SearchArtistsResponse struct {
func (x *SearchArtistsResponse) Reset() {
*x = SearchArtistsResponse{}
mi := &file_metadata_v1_metadata_proto_msgTypes[7]
mi := &file_metadata_v1_metadata_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -653,7 +781,7 @@ func (x *SearchArtistsResponse) String() string {
func (*SearchArtistsResponse) ProtoMessage() {}
func (x *SearchArtistsResponse) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[7]
mi := &file_metadata_v1_metadata_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -666,7 +794,7 @@ func (x *SearchArtistsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use SearchArtistsResponse.ProtoReflect.Descriptor instead.
func (*SearchArtistsResponse) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{7}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{9}
}
func (x *SearchArtistsResponse) GetArtists() []*Artist {
@@ -693,7 +821,7 @@ type GetArtistAlbumsResponse struct {
func (x *GetArtistAlbumsResponse) Reset() {
*x = GetArtistAlbumsResponse{}
mi := &file_metadata_v1_metadata_proto_msgTypes[8]
mi := &file_metadata_v1_metadata_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -705,7 +833,7 @@ func (x *GetArtistAlbumsResponse) String() string {
func (*GetArtistAlbumsResponse) ProtoMessage() {}
func (x *GetArtistAlbumsResponse) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[8]
mi := &file_metadata_v1_metadata_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -718,7 +846,7 @@ func (x *GetArtistAlbumsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetArtistAlbumsResponse.ProtoReflect.Descriptor instead.
func (*GetArtistAlbumsResponse) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{8}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{10}
}
func (x *GetArtistAlbumsResponse) GetAlbums() []*Album {
@@ -744,7 +872,7 @@ type GetAlbumTracksResponse struct {
func (x *GetAlbumTracksResponse) Reset() {
*x = GetAlbumTracksResponse{}
mi := &file_metadata_v1_metadata_proto_msgTypes[9]
mi := &file_metadata_v1_metadata_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -756,7 +884,7 @@ func (x *GetAlbumTracksResponse) String() string {
func (*GetAlbumTracksResponse) ProtoMessage() {}
func (x *GetAlbumTracksResponse) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[9]
mi := &file_metadata_v1_metadata_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -769,7 +897,7 @@ func (x *GetAlbumTracksResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetAlbumTracksResponse.ProtoReflect.Descriptor instead.
func (*GetAlbumTracksResponse) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{9}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{11}
}
func (x *GetAlbumTracksResponse) GetTracks() []*Track {
@@ -790,7 +918,7 @@ type SyncArtistResponse struct {
func (x *SyncArtistResponse) Reset() {
*x = SyncArtistResponse{}
mi := &file_metadata_v1_metadata_proto_msgTypes[10]
mi := &file_metadata_v1_metadata_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -802,7 +930,7 @@ func (x *SyncArtistResponse) String() string {
func (*SyncArtistResponse) ProtoMessage() {}
func (x *SyncArtistResponse) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[10]
mi := &file_metadata_v1_metadata_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -815,7 +943,7 @@ func (x *SyncArtistResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use SyncArtistResponse.ProtoReflect.Descriptor instead.
func (*SyncArtistResponse) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{10}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{12}
}
func (x *SyncArtistResponse) GetArtist() *Artist {
@@ -858,7 +986,7 @@ type Artist struct {
func (x *Artist) Reset() {
*x = Artist{}
mi := &file_metadata_v1_metadata_proto_msgTypes[11]
mi := &file_metadata_v1_metadata_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -870,7 +998,7 @@ func (x *Artist) String() string {
func (*Artist) ProtoMessage() {}
func (x *Artist) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[11]
mi := &file_metadata_v1_metadata_proto_msgTypes[13]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -883,7 +1011,7 @@ func (x *Artist) ProtoReflect() protoreflect.Message {
// Deprecated: Use Artist.ProtoReflect.Descriptor instead.
func (*Artist) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{11}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{13}
}
func (x *Artist) GetId() string {
@@ -983,7 +1111,7 @@ type Album struct {
func (x *Album) Reset() {
*x = Album{}
mi := &file_metadata_v1_metadata_proto_msgTypes[12]
mi := &file_metadata_v1_metadata_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -995,7 +1123,7 @@ func (x *Album) String() string {
func (*Album) ProtoMessage() {}
func (x *Album) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[12]
mi := &file_metadata_v1_metadata_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1008,7 +1136,7 @@ func (x *Album) ProtoReflect() protoreflect.Message {
// Deprecated: Use Album.ProtoReflect.Descriptor instead.
func (*Album) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{12}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{14}
}
func (x *Album) GetId() string {
@@ -1113,7 +1241,7 @@ type Track struct {
func (x *Track) Reset() {
*x = Track{}
mi := &file_metadata_v1_metadata_proto_msgTypes[13]
mi := &file_metadata_v1_metadata_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1125,7 +1253,7 @@ func (x *Track) String() string {
func (*Track) ProtoMessage() {}
func (x *Track) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[13]
mi := &file_metadata_v1_metadata_proto_msgTypes[15]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1138,7 +1266,7 @@ func (x *Track) ProtoReflect() protoreflect.Message {
// Deprecated: Use Track.ProtoReflect.Descriptor instead.
func (*Track) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{13}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{15}
}
func (x *Track) GetId() string {
@@ -1224,7 +1352,7 @@ type Work struct {
func (x *Work) Reset() {
*x = Work{}
mi := &file_metadata_v1_metadata_proto_msgTypes[14]
mi := &file_metadata_v1_metadata_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1236,7 +1364,7 @@ func (x *Work) String() string {
func (*Work) ProtoMessage() {}
func (x *Work) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[14]
mi := &file_metadata_v1_metadata_proto_msgTypes[16]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1249,7 +1377,7 @@ func (x *Work) ProtoReflect() protoreflect.Message {
// Deprecated: Use Work.ProtoReflect.Descriptor instead.
func (*Work) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{14}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{16}
}
func (x *Work) GetId() string {
@@ -1298,7 +1426,7 @@ type Label struct {
func (x *Label) Reset() {
*x = Label{}
mi := &file_metadata_v1_metadata_proto_msgTypes[15]
mi := &file_metadata_v1_metadata_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1310,7 +1438,7 @@ func (x *Label) String() string {
func (*Label) ProtoMessage() {}
func (x *Label) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[15]
mi := &file_metadata_v1_metadata_proto_msgTypes[17]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1323,7 +1451,7 @@ func (x *Label) ProtoReflect() protoreflect.Message {
// Deprecated: Use Label.ProtoReflect.Descriptor instead.
func (*Label) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{15}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{17}
}
func (x *Label) GetId() string {
@@ -1357,7 +1485,7 @@ type Genre struct {
func (x *Genre) Reset() {
*x = Genre{}
mi := &file_metadata_v1_metadata_proto_msgTypes[16]
mi := &file_metadata_v1_metadata_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1369,7 +1497,7 @@ func (x *Genre) String() string {
func (*Genre) ProtoMessage() {}
func (x *Genre) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[16]
mi := &file_metadata_v1_metadata_proto_msgTypes[18]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1382,7 +1510,7 @@ func (x *Genre) ProtoReflect() protoreflect.Message {
// Deprecated: Use Genre.ProtoReflect.Descriptor instead.
func (*Genre) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{16}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{18}
}
func (x *Genre) GetId() string {
@@ -1411,7 +1539,7 @@ type ArtistCredit struct {
func (x *ArtistCredit) Reset() {
*x = ArtistCredit{}
mi := &file_metadata_v1_metadata_proto_msgTypes[17]
mi := &file_metadata_v1_metadata_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1423,7 +1551,7 @@ func (x *ArtistCredit) String() string {
func (*ArtistCredit) ProtoMessage() {}
func (x *ArtistCredit) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[17]
mi := &file_metadata_v1_metadata_proto_msgTypes[19]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1436,7 +1564,7 @@ func (x *ArtistCredit) ProtoReflect() protoreflect.Message {
// Deprecated: Use ArtistCredit.ProtoReflect.Descriptor instead.
func (*ArtistCredit) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{17}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{19}
}
func (x *ArtistCredit) GetArtist() *Artist {
@@ -1478,7 +1606,7 @@ type ExternalID struct {
func (x *ExternalID) Reset() {
*x = ExternalID{}
mi := &file_metadata_v1_metadata_proto_msgTypes[18]
mi := &file_metadata_v1_metadata_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1490,7 +1618,7 @@ func (x *ExternalID) String() string {
func (*ExternalID) ProtoMessage() {}
func (x *ExternalID) ProtoReflect() protoreflect.Message {
mi := &file_metadata_v1_metadata_proto_msgTypes[18]
mi := &file_metadata_v1_metadata_proto_msgTypes[20]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1503,7 +1631,7 @@ func (x *ExternalID) ProtoReflect() protoreflect.Message {
// Deprecated: Use ExternalID.ProtoReflect.Descriptor instead.
func (*ExternalID) Descriptor() ([]byte, []int) {
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{18}
return file_metadata_v1_metadata_proto_rawDescGZIP(), []int{20}
}
func (x *ExternalID) GetSource() string {
@@ -1563,7 +1691,16 @@ const file_metadata_v1_metadata_proto_rawDesc = "" +
"identifier\"e\n" +
"\x15GetAlbumTracksRequest\x12\x19\n" +
"\balbum_id\x18\x01 \x01(\tR\aalbumId\x121\n" +
"\bprovider\x18\x02 \x01(\x0e2\x15.metadata.v1.ProviderR\bprovider\"\x9d\x01\n" +
"\bprovider\x18\x02 \x01(\x0e2\x15.metadata.v1.ProviderR\bprovider\"\xa4\x01\n" +
"\x13SearchAlbumsRequest\x12\x14\n" +
"\x05query\x18\x01 \x01(\tR\x05query\x12\x16\n" +
"\x06artist\x18\x02 \x01(\tR\x06artist\x12\x14\n" +
"\x05limit\x18\x03 \x01(\x05R\x05limit\x12\x16\n" +
"\x06offset\x18\x04 \x01(\x05R\x06offset\x121\n" +
"\bprovider\x18\x05 \x01(\x0e2\x15.metadata.v1.ProviderR\bprovider\"X\n" +
"\x14SearchAlbumsResponse\x12*\n" +
"\x06albums\x18\x01 \x03(\v2\x12.metadata.v1.AlbumR\x06albums\x12\x14\n" +
"\x05total\x18\x02 \x01(\x05R\x05total\"\x9d\x01\n" +
"\x11SyncArtistRequest\x12\x14\n" +
"\x04name\x18\x01 \x01(\tH\x00R\x04name\x125\n" +
"\bexternal\x18\x02 \x01(\v2\x17.metadata.v1.ExternalIDH\x00R\bexternal\x121\n" +
@@ -1652,14 +1789,15 @@ const file_metadata_v1_metadata_proto_rawDesc = "" +
"\x03url\x18\x03 \x01(\tR\x03url*>\n" +
"\bProvider\x12\x18\n" +
"\x14PROVIDER_UNSPECIFIED\x10\x00\x12\x18\n" +
"\x14PROVIDER_MUSICBRAINZ\x10\x012\xae\x04\n" +
"\x14PROVIDER_MUSICBRAINZ\x10\x012\x83\x05\n" +
"\x0fMetadataService\x12?\n" +
"\tGetArtist\x12\x1d.metadata.v1.GetArtistRequest\x1a\x13.metadata.v1.Artist\x12V\n" +
"\rSearchArtists\x12!.metadata.v1.SearchArtistsRequest\x1a\".metadata.v1.SearchArtistsResponse\x12<\n" +
"\bGetAlbum\x12\x1c.metadata.v1.GetAlbumRequest\x1a\x12.metadata.v1.Album\x12\\\n" +
"\x0fGetArtistAlbums\x12#.metadata.v1.GetArtistAlbumsRequest\x1a$.metadata.v1.GetArtistAlbumsResponse\x12<\n" +
"\bGetTrack\x12\x1c.metadata.v1.GetTrackRequest\x1a\x12.metadata.v1.Track\x12Y\n" +
"\x0eGetAlbumTracks\x12\".metadata.v1.GetAlbumTracksRequest\x1a#.metadata.v1.GetAlbumTracksResponse\x12M\n" +
"\x0eGetAlbumTracks\x12\".metadata.v1.GetAlbumTracksRequest\x1a#.metadata.v1.GetAlbumTracksResponse\x12S\n" +
"\fSearchAlbums\x12 .metadata.v1.SearchAlbumsRequest\x1a!.metadata.v1.SearchAlbumsResponse\x12M\n" +
"\n" +
"SyncArtist\x12\x1e.metadata.v1.SyncArtistRequest\x1a\x1f.metadata.v1.SyncArtistResponseB\xab\x01\n" +
"\x0fcom.metadata.v1B\rMetadataProtoP\x01Z<github.com/metadata-agregator/pkg/gen/metadata/v1;metadatav1\xa2\x02\x03MXX\xaa\x02\vMetadata.V1\xca\x02\vMetadata\\V1\xe2\x02\x17Metadata\\V1\\GPBMetadata\xea\x02\fMetadata::V1b\x06proto3"
@@ -1677,7 +1815,7 @@ func file_metadata_v1_metadata_proto_rawDescGZIP() []byte {
}
var file_metadata_v1_metadata_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_metadata_v1_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
var file_metadata_v1_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
var file_metadata_v1_metadata_proto_goTypes = []any{
(Provider)(0), // 0: metadata.v1.Provider
(*GetArtistRequest)(nil), // 1: metadata.v1.GetArtistRequest
@@ -1686,66 +1824,72 @@ var file_metadata_v1_metadata_proto_goTypes = []any{
(*GetArtistAlbumsRequest)(nil), // 4: metadata.v1.GetArtistAlbumsRequest
(*GetTrackRequest)(nil), // 5: metadata.v1.GetTrackRequest
(*GetAlbumTracksRequest)(nil), // 6: metadata.v1.GetAlbumTracksRequest
(*SyncArtistRequest)(nil), // 7: metadata.v1.SyncArtistRequest
(*SearchArtistsResponse)(nil), // 8: metadata.v1.SearchArtistsResponse
(*GetArtistAlbumsResponse)(nil), // 9: metadata.v1.GetArtistAlbumsResponse
(*GetAlbumTracksResponse)(nil), // 10: metadata.v1.GetAlbumTracksResponse
(*SyncArtistResponse)(nil), // 11: metadata.v1.SyncArtistResponse
(*Artist)(nil), // 12: metadata.v1.Artist
(*Album)(nil), // 13: metadata.v1.Album
(*Track)(nil), // 14: metadata.v1.Track
(*Work)(nil), // 15: metadata.v1.Work
(*Label)(nil), // 16: metadata.v1.Label
(*Genre)(nil), // 17: metadata.v1.Genre
(*ArtistCredit)(nil), // 18: metadata.v1.ArtistCredit
(*ExternalID)(nil), // 19: metadata.v1.ExternalID
(*SearchAlbumsRequest)(nil), // 7: metadata.v1.SearchAlbumsRequest
(*SearchAlbumsResponse)(nil), // 8: metadata.v1.SearchAlbumsResponse
(*SyncArtistRequest)(nil), // 9: metadata.v1.SyncArtistRequest
(*SearchArtistsResponse)(nil), // 10: metadata.v1.SearchArtistsResponse
(*GetArtistAlbumsResponse)(nil), // 11: metadata.v1.GetArtistAlbumsResponse
(*GetAlbumTracksResponse)(nil), // 12: metadata.v1.GetAlbumTracksResponse
(*SyncArtistResponse)(nil), // 13: metadata.v1.SyncArtistResponse
(*Artist)(nil), // 14: metadata.v1.Artist
(*Album)(nil), // 15: metadata.v1.Album
(*Track)(nil), // 16: metadata.v1.Track
(*Work)(nil), // 17: metadata.v1.Work
(*Label)(nil), // 18: metadata.v1.Label
(*Genre)(nil), // 19: metadata.v1.Genre
(*ArtistCredit)(nil), // 20: metadata.v1.ArtistCredit
(*ExternalID)(nil), // 21: metadata.v1.ExternalID
}
var file_metadata_v1_metadata_proto_depIdxs = []int32{
19, // 0: metadata.v1.GetArtistRequest.external:type_name -> metadata.v1.ExternalID
21, // 0: metadata.v1.GetArtistRequest.external:type_name -> metadata.v1.ExternalID
0, // 1: metadata.v1.GetArtistRequest.provider:type_name -> metadata.v1.Provider
0, // 2: metadata.v1.SearchArtistsRequest.provider:type_name -> metadata.v1.Provider
19, // 3: metadata.v1.GetAlbumRequest.external:type_name -> metadata.v1.ExternalID
21, // 3: metadata.v1.GetAlbumRequest.external:type_name -> metadata.v1.ExternalID
0, // 4: metadata.v1.GetAlbumRequest.provider:type_name -> metadata.v1.Provider
0, // 5: metadata.v1.GetArtistAlbumsRequest.provider:type_name -> metadata.v1.Provider
19, // 6: metadata.v1.GetTrackRequest.external:type_name -> metadata.v1.ExternalID
21, // 6: metadata.v1.GetTrackRequest.external:type_name -> metadata.v1.ExternalID
0, // 7: metadata.v1.GetTrackRequest.provider:type_name -> metadata.v1.Provider
0, // 8: metadata.v1.GetAlbumTracksRequest.provider:type_name -> metadata.v1.Provider
19, // 9: metadata.v1.SyncArtistRequest.external:type_name -> metadata.v1.ExternalID
0, // 10: metadata.v1.SyncArtistRequest.provider:type_name -> metadata.v1.Provider
12, // 11: metadata.v1.SearchArtistsResponse.artists:type_name -> metadata.v1.Artist
13, // 12: metadata.v1.GetArtistAlbumsResponse.albums:type_name -> metadata.v1.Album
14, // 13: metadata.v1.GetAlbumTracksResponse.tracks:type_name -> metadata.v1.Track
12, // 14: metadata.v1.SyncArtistResponse.artist:type_name -> metadata.v1.Artist
17, // 15: metadata.v1.Artist.genres:type_name -> metadata.v1.Genre
19, // 16: metadata.v1.Artist.external_ids:type_name -> metadata.v1.ExternalID
18, // 17: metadata.v1.Album.artists:type_name -> metadata.v1.ArtistCredit
16, // 18: metadata.v1.Album.label:type_name -> metadata.v1.Label
17, // 19: metadata.v1.Album.genres:type_name -> metadata.v1.Genre
19, // 20: metadata.v1.Album.external_ids:type_name -> metadata.v1.ExternalID
18, // 21: metadata.v1.Track.artists:type_name -> metadata.v1.ArtistCredit
15, // 22: metadata.v1.Track.work:type_name -> metadata.v1.Work
19, // 23: metadata.v1.Track.external_ids:type_name -> metadata.v1.ExternalID
18, // 24: metadata.v1.Work.composers:type_name -> metadata.v1.ArtistCredit
12, // 25: metadata.v1.ArtistCredit.artist:type_name -> metadata.v1.Artist
1, // 26: metadata.v1.MetadataService.GetArtist:input_type -> metadata.v1.GetArtistRequest
2, // 27: metadata.v1.MetadataService.SearchArtists:input_type -> metadata.v1.SearchArtistsRequest
3, // 28: metadata.v1.MetadataService.GetAlbum:input_type -> metadata.v1.GetAlbumRequest
4, // 29: metadata.v1.MetadataService.GetArtistAlbums:input_type -> metadata.v1.GetArtistAlbumsRequest
5, // 30: metadata.v1.MetadataService.GetTrack:input_type -> metadata.v1.GetTrackRequest
6, // 31: metadata.v1.MetadataService.GetAlbumTracks:input_type -> metadata.v1.GetAlbumTracksRequest
7, // 32: metadata.v1.MetadataService.SyncArtist:input_type -> metadata.v1.SyncArtistRequest
12, // 33: metadata.v1.MetadataService.GetArtist:output_type -> metadata.v1.Artist
8, // 34: metadata.v1.MetadataService.SearchArtists:output_type -> metadata.v1.SearchArtistsResponse
13, // 35: metadata.v1.MetadataService.GetAlbum:output_type -> metadata.v1.Album
9, // 36: metadata.v1.MetadataService.GetArtistAlbums:output_type -> metadata.v1.GetArtistAlbumsResponse
14, // 37: metadata.v1.MetadataService.GetTrack:output_type -> metadata.v1.Track
10, // 38: metadata.v1.MetadataService.GetAlbumTracks:output_type -> metadata.v1.GetAlbumTracksResponse
11, // 39: metadata.v1.MetadataService.SyncArtist:output_type -> metadata.v1.SyncArtistResponse
33, // [33:40] is the sub-list for method output_type
26, // [26:33] is the sub-list for method input_type
26, // [26:26] is the sub-list for extension type_name
26, // [26:26] is the sub-list for extension extendee
0, // [0:26] is the sub-list for field type_name
0, // 9: metadata.v1.SearchAlbumsRequest.provider:type_name -> metadata.v1.Provider
15, // 10: metadata.v1.SearchAlbumsResponse.albums:type_name -> metadata.v1.Album
21, // 11: metadata.v1.SyncArtistRequest.external:type_name -> metadata.v1.ExternalID
0, // 12: metadata.v1.SyncArtistRequest.provider:type_name -> metadata.v1.Provider
14, // 13: metadata.v1.SearchArtistsResponse.artists:type_name -> metadata.v1.Artist
15, // 14: metadata.v1.GetArtistAlbumsResponse.albums:type_name -> metadata.v1.Album
16, // 15: metadata.v1.GetAlbumTracksResponse.tracks:type_name -> metadata.v1.Track
14, // 16: metadata.v1.SyncArtistResponse.artist:type_name -> metadata.v1.Artist
19, // 17: metadata.v1.Artist.genres:type_name -> metadata.v1.Genre
21, // 18: metadata.v1.Artist.external_ids:type_name -> metadata.v1.ExternalID
20, // 19: metadata.v1.Album.artists:type_name -> metadata.v1.ArtistCredit
18, // 20: metadata.v1.Album.label:type_name -> metadata.v1.Label
19, // 21: metadata.v1.Album.genres:type_name -> metadata.v1.Genre
21, // 22: metadata.v1.Album.external_ids:type_name -> metadata.v1.ExternalID
20, // 23: metadata.v1.Track.artists:type_name -> metadata.v1.ArtistCredit
17, // 24: metadata.v1.Track.work:type_name -> metadata.v1.Work
21, // 25: metadata.v1.Track.external_ids:type_name -> metadata.v1.ExternalID
20, // 26: metadata.v1.Work.composers:type_name -> metadata.v1.ArtistCredit
14, // 27: metadata.v1.ArtistCredit.artist:type_name -> metadata.v1.Artist
1, // 28: metadata.v1.MetadataService.GetArtist:input_type -> metadata.v1.GetArtistRequest
2, // 29: metadata.v1.MetadataService.SearchArtists:input_type -> metadata.v1.SearchArtistsRequest
3, // 30: metadata.v1.MetadataService.GetAlbum:input_type -> metadata.v1.GetAlbumRequest
4, // 31: metadata.v1.MetadataService.GetArtistAlbums:input_type -> metadata.v1.GetArtistAlbumsRequest
5, // 32: metadata.v1.MetadataService.GetTrack:input_type -> metadata.v1.GetTrackRequest
6, // 33: metadata.v1.MetadataService.GetAlbumTracks:input_type -> metadata.v1.GetAlbumTracksRequest
7, // 34: metadata.v1.MetadataService.SearchAlbums:input_type -> metadata.v1.SearchAlbumsRequest
9, // 35: metadata.v1.MetadataService.SyncArtist:input_type -> metadata.v1.SyncArtistRequest
14, // 36: metadata.v1.MetadataService.GetArtist:output_type -> metadata.v1.Artist
10, // 37: metadata.v1.MetadataService.SearchArtists:output_type -> metadata.v1.SearchArtistsResponse
15, // 38: metadata.v1.MetadataService.GetAlbum:output_type -> metadata.v1.Album
11, // 39: metadata.v1.MetadataService.GetArtistAlbums:output_type -> metadata.v1.GetArtistAlbumsResponse
16, // 40: metadata.v1.MetadataService.GetTrack:output_type -> metadata.v1.Track
12, // 41: metadata.v1.MetadataService.GetAlbumTracks:output_type -> metadata.v1.GetAlbumTracksResponse
8, // 42: metadata.v1.MetadataService.SearchAlbums:output_type -> metadata.v1.SearchAlbumsResponse
13, // 43: metadata.v1.MetadataService.SyncArtist:output_type -> metadata.v1.SyncArtistResponse
36, // [36:44] is the sub-list for method output_type
28, // [28:36] is the sub-list for method input_type
28, // [28:28] is the sub-list for extension type_name
28, // [28:28] is the sub-list for extension extendee
0, // [0:28] is the sub-list for field type_name
}
func init() { file_metadata_v1_metadata_proto_init() }
@@ -1766,7 +1910,7 @@ func file_metadata_v1_metadata_proto_init() {
(*GetTrackRequest_External)(nil),
(*GetTrackRequest_Isrc)(nil),
}
file_metadata_v1_metadata_proto_msgTypes[6].OneofWrappers = []any{
file_metadata_v1_metadata_proto_msgTypes[8].OneofWrappers = []any{
(*SyncArtistRequest_Name)(nil),
(*SyncArtistRequest_External)(nil),
}
@@ -1776,7 +1920,7 @@ func file_metadata_v1_metadata_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_metadata_v1_metadata_proto_rawDesc), len(file_metadata_v1_metadata_proto_rawDesc)),
NumEnums: 1,
NumMessages: 19,
NumMessages: 21,
NumExtensions: 0,
NumServices: 1,
},
+40
View File
@@ -25,6 +25,7 @@ const (
MetadataService_GetArtistAlbums_FullMethodName = "/metadata.v1.MetadataService/GetArtistAlbums"
MetadataService_GetTrack_FullMethodName = "/metadata.v1.MetadataService/GetTrack"
MetadataService_GetAlbumTracks_FullMethodName = "/metadata.v1.MetadataService/GetAlbumTracks"
MetadataService_SearchAlbums_FullMethodName = "/metadata.v1.MetadataService/SearchAlbums"
MetadataService_SyncArtist_FullMethodName = "/metadata.v1.MetadataService/SyncArtist"
)
@@ -46,6 +47,8 @@ type MetadataServiceClient interface {
GetTrack(ctx context.Context, in *GetTrackRequest, opts ...grpc.CallOption) (*Track, error)
// GetAlbumTracks retrieves all tracks on an album.
GetAlbumTracks(ctx context.Context, in *GetAlbumTracksRequest, opts ...grpc.CallOption) (*GetAlbumTracksResponse, error)
// SearchAlbums searches for albums by name, optionally filtered by artist.
SearchAlbums(ctx context.Context, in *SearchAlbumsRequest, opts ...grpc.CallOption) (*SearchAlbumsResponse, error)
// SyncArtist triggers ingestion of an artist from external sources.
SyncArtist(ctx context.Context, in *SyncArtistRequest, opts ...grpc.CallOption) (*SyncArtistResponse, error)
}
@@ -118,6 +121,16 @@ func (c *metadataServiceClient) GetAlbumTracks(ctx context.Context, in *GetAlbum
return out, nil
}
func (c *metadataServiceClient) SearchAlbums(ctx context.Context, in *SearchAlbumsRequest, opts ...grpc.CallOption) (*SearchAlbumsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SearchAlbumsResponse)
err := c.cc.Invoke(ctx, MetadataService_SearchAlbums_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *metadataServiceClient) SyncArtist(ctx context.Context, in *SyncArtistRequest, opts ...grpc.CallOption) (*SyncArtistResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SyncArtistResponse)
@@ -146,6 +159,8 @@ type MetadataServiceServer interface {
GetTrack(context.Context, *GetTrackRequest) (*Track, error)
// GetAlbumTracks retrieves all tracks on an album.
GetAlbumTracks(context.Context, *GetAlbumTracksRequest) (*GetAlbumTracksResponse, error)
// SearchAlbums searches for albums by name, optionally filtered by artist.
SearchAlbums(context.Context, *SearchAlbumsRequest) (*SearchAlbumsResponse, error)
// SyncArtist triggers ingestion of an artist from external sources.
SyncArtist(context.Context, *SyncArtistRequest) (*SyncArtistResponse, error)
mustEmbedUnimplementedMetadataServiceServer()
@@ -176,6 +191,9 @@ func (UnimplementedMetadataServiceServer) GetTrack(context.Context, *GetTrackReq
func (UnimplementedMetadataServiceServer) GetAlbumTracks(context.Context, *GetAlbumTracksRequest) (*GetAlbumTracksResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetAlbumTracks not implemented")
}
func (UnimplementedMetadataServiceServer) SearchAlbums(context.Context, *SearchAlbumsRequest) (*SearchAlbumsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method SearchAlbums not implemented")
}
func (UnimplementedMetadataServiceServer) SyncArtist(context.Context, *SyncArtistRequest) (*SyncArtistResponse, error) {
return nil, status.Error(codes.Unimplemented, "method SyncArtist not implemented")
}
@@ -308,6 +326,24 @@ func _MetadataService_GetAlbumTracks_Handler(srv interface{}, ctx context.Contex
return interceptor(ctx, in, info, handler)
}
func _MetadataService_SearchAlbums_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SearchAlbumsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MetadataServiceServer).SearchAlbums(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MetadataService_SearchAlbums_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MetadataServiceServer).SearchAlbums(ctx, req.(*SearchAlbumsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MetadataService_SyncArtist_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SyncArtistRequest)
if err := dec(in); err != nil {
@@ -357,6 +393,10 @@ var MetadataService_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetAlbumTracks",
Handler: _MetadataService_GetAlbumTracks_Handler,
},
{
MethodName: "SearchAlbums",
Handler: _MetadataService_SearchAlbums_Handler,
},
{
MethodName: "SyncArtist",
Handler: _MetadataService_SyncArtist_Handler,
+16
View File
@@ -29,6 +29,9 @@ service MetadataService {
// GetAlbumTracks retrieves all tracks on an album.
rpc GetAlbumTracks(GetAlbumTracksRequest) returns (GetAlbumTracksResponse);
// SearchAlbums searches for albums by name, optionally filtered by artist.
rpc SearchAlbums(SearchAlbumsRequest) returns (SearchAlbumsResponse);
// SyncArtist triggers ingestion of an artist from external sources.
rpc SyncArtist(SyncArtistRequest) returns (SyncArtistResponse);
}
@@ -79,6 +82,19 @@ message GetAlbumTracksRequest {
Provider provider = 2;
}
message SearchAlbumsRequest {
string query = 1;
string artist = 2;
int32 limit = 3;
int32 offset = 4;
Provider provider = 5;
}
message SearchAlbumsResponse {
repeated Album albums = 1;
int32 total = 2;
}
message SyncArtistRequest {
oneof target {
string name = 1;