Compare commits
3 Commits
a1f6701bac
...
fb2e4b9107
| Author | SHA1 | Date | |
|---|---|---|---|
| fb2e4b9107 | |||
| de52fcbc79 | |||
| de674376ed |
@@ -5,8 +5,10 @@ server
|
||||
*.test
|
||||
*.out
|
||||
.env
|
||||
config.yaml
|
||||
*.log
|
||||
vendor/
|
||||
.pre-commit-config.yaml
|
||||
|
||||
docs/research/*/repo/
|
||||
docs/research/*/repo-index/
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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,3 +1,4 @@
|
||||
listen_addresses = '*'
|
||||
shared_preload_libraries = 'pg_prewarm'
|
||||
|
||||
pg_prewarm.autoprewarm = true
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user