feat: filter artist albums by type, default to album|ep|single
This commit is contained in:
@@ -137,12 +137,17 @@ func (c *client) lookup(ctx context.Context, entity, id string, inc []string) ([
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) browse(ctx context.Context, entity, linkedEntity, linkedID string, limit, offset int, inc []string) ([]byte, error) {
|
func (c *client) browse(ctx context.Context, entity, linkedEntity, linkedID string, limit, offset int, inc []string) ([]byte, error) {
|
||||||
|
return c.browseWithTypes(ctx, entity, linkedEntity, linkedID, limit, offset, inc, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) browseWithTypes(ctx context.Context, entity, linkedEntity, linkedID string, limit, offset int, inc []string, types []string) ([]byte, error) {
|
||||||
zerolog.Ctx(ctx).Debug().
|
zerolog.Ctx(ctx).Debug().
|
||||||
Str("entity", entity).
|
Str("entity", entity).
|
||||||
Str("linked_entity", linkedEntity).
|
Str("linked_entity", linkedEntity).
|
||||||
Str("linked_id", linkedID).
|
Str("linked_id", linkedID).
|
||||||
Int("limit", limit).
|
Int("limit", limit).
|
||||||
Int("offset", offset).
|
Int("offset", offset).
|
||||||
|
Strs("types", types).
|
||||||
Msg("provider browse")
|
Msg("provider browse")
|
||||||
|
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
@@ -154,6 +159,10 @@ func (c *client) browse(ctx context.Context, entity, linkedEntity, linkedID stri
|
|||||||
params.Set("inc", joined)
|
params.Set("inc", joined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if typeStr := strings.Join(types, "|"); typeStr != "" {
|
||||||
|
params.Set("type", typeStr)
|
||||||
|
}
|
||||||
|
|
||||||
return c.get(ctx, entity, params)
|
return c.get(ctx, entity, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func (p *Provider) GetAlbum(ctx context.Context, id string) (*domain.Album, erro
|
|||||||
return mapAlbum(mb, release), nil
|
return mapAlbum(mb, release), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) SearchAlbums(ctx context.Context, query string, artist string, limit, offset int) (*domain.SearchResult[domain.Album], error) {
|
func (p *Provider) SearchAlbums(ctx context.Context, query string, artist string, limit, offset int, albumTypes []string) (*domain.SearchResult[domain.Album], error) {
|
||||||
if limit <= 0 || limit > 100 {
|
if limit <= 0 || limit > 100 {
|
||||||
limit = 25
|
limit = 25
|
||||||
}
|
}
|
||||||
@@ -105,6 +105,10 @@ func (p *Provider) SearchAlbums(ctx context.Context, query string, artist string
|
|||||||
luceneQuery = fmt.Sprintf("releasegroup:%s", escapeQuery(query))
|
luceneQuery = fmt.Sprintf("releasegroup:%s", escapeQuery(query))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if typeFilter := buildTypeFilter(albumTypes); typeFilter != "" {
|
||||||
|
luceneQuery += " AND " + typeFilter
|
||||||
|
}
|
||||||
|
|
||||||
data, err := p.client.search(ctx, "release-group", luceneQuery, limit, offset)
|
data, err := p.client.search(ctx, "release-group", luceneQuery, limit, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("search albums: %w", err)
|
return nil, fmt.Errorf("search albums: %w", err)
|
||||||
@@ -134,12 +138,12 @@ func (p *Provider) SearchAlbums(ctx context.Context, query string, artist string
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) GetArtistAlbums(ctx context.Context, artistID string, limit, offset int) (*domain.SearchResult[domain.Album], error) {
|
func (p *Provider) GetArtistAlbums(ctx context.Context, artistID string, limit, offset int, albumTypes []string) (*domain.SearchResult[domain.Album], error) {
|
||||||
if limit <= 0 || limit > 100 {
|
if limit <= 0 || limit > 100 {
|
||||||
limit = 25
|
limit = 25
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := p.client.browse(ctx, "release-group", "artist", artistID, limit, offset, []string{"artist-credits"})
|
data, err := p.client.browseWithTypes(ctx, "release-group", "artist", artistID, limit, offset, []string{"artist-credits"}, albumTypes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("browse release-groups: %w", err)
|
return nil, fmt.Errorf("browse release-groups: %w", err)
|
||||||
}
|
}
|
||||||
@@ -311,6 +315,20 @@ func selectCanonicalRelease(releases []*mbRelease) *mbRelease {
|
|||||||
return best
|
return best
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildTypeFilter(types []string) string {
|
||||||
|
if len(types) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(types) == 1 {
|
||||||
|
return fmt.Sprintf("primarytype:%s", types[0])
|
||||||
|
}
|
||||||
|
escaped := make([]string, len(types))
|
||||||
|
for i, t := range types {
|
||||||
|
escaped[i] = fmt.Sprintf("primarytype:%s", t)
|
||||||
|
}
|
||||||
|
return "(" + strings.Join(escaped, " OR ") + ")"
|
||||||
|
}
|
||||||
|
|
||||||
func escapeQuery(s string) string {
|
func escapeQuery(s string) string {
|
||||||
special := []string{`+`, `-`, `&`, `|`, `!`, `(`, `)`, `{`, `}`, `[`, `]`, `^`, `"`, `~`, `*`, `?`, `:`, `/`, `\`}
|
special := []string{`+`, `-`, `&`, `|`, `!`, `(`, `)`, `{`, `}`, `[`, `]`, `^`, `"`, `~`, `*`, `?`, `:`, `/`, `\`}
|
||||||
result := s
|
result := s
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ type Provider interface {
|
|||||||
SearchArtists(ctx context.Context, query string, limit, offset int) (*domain.SearchResult[domain.Artist], error)
|
SearchArtists(ctx context.Context, query string, limit, offset int) (*domain.SearchResult[domain.Artist], error)
|
||||||
|
|
||||||
GetAlbum(ctx context.Context, id string) (*domain.Album, 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)
|
SearchAlbums(ctx context.Context, query string, artist string, limit, offset int, albumTypes []string) (*domain.SearchResult[domain.Album], error)
|
||||||
GetArtistAlbums(ctx context.Context, artistID string, limit, offset int) (*domain.SearchResult[domain.Album], error)
|
GetArtistAlbums(ctx context.Context, artistID string, limit, offset int, albumTypes []string) (*domain.SearchResult[domain.Album], error)
|
||||||
|
|
||||||
GetTrack(ctx context.Context, id string) (*domain.Track, error)
|
GetTrack(ctx context.Context, id string) (*domain.Track, error)
|
||||||
GetAlbumTracks(ctx context.Context, albumID string) ([]domain.Track, error)
|
GetAlbumTracks(ctx context.Context, albumID string) ([]domain.Track, error)
|
||||||
|
|||||||
@@ -114,9 +114,10 @@ func (s *MetadataServer) SearchAlbums(ctx context.Context, req *metadatav1.Searc
|
|||||||
limit = 25
|
limit = 25
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Str("query", req.Query).Str("artist", req.Artist).Int("limit", limit).Int("offset", int(req.Offset)).Msg("searching albums")
|
albumTypes := defaultAlbumTypes(req.AlbumTypes)
|
||||||
|
log.Debug().Str("query", req.Query).Str("artist", req.Artist).Int("limit", limit).Int("offset", int(req.Offset)).Strs("album_types", albumTypes).Msg("searching albums")
|
||||||
|
|
||||||
result, err := svc.SearchAlbums(ctx, req.Query, req.Artist, limit, int(req.Offset))
|
result, err := svc.SearchAlbums(ctx, req.Query, req.Artist, limit, int(req.Offset), albumTypes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toGRPCError(ctx, err)
|
return nil, toGRPCError(ctx, err)
|
||||||
}
|
}
|
||||||
@@ -176,9 +177,10 @@ func (s *MetadataServer) GetArtistAlbums(ctx context.Context, req *metadatav1.Ge
|
|||||||
limit = 25
|
limit = 25
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Str("artist_id", req.ArtistId).Int("limit", limit).Int("offset", int(req.Offset)).Msg("getting artist albums")
|
albumTypes := defaultAlbumTypes(req.AlbumTypes)
|
||||||
|
log.Debug().Str("artist_id", req.ArtistId).Int("limit", limit).Int("offset", int(req.Offset)).Strs("album_types", albumTypes).Msg("getting artist albums")
|
||||||
|
|
||||||
result, err := svc.GetArtistAlbums(ctx, req.ArtistId, limit, int(req.Offset))
|
result, err := svc.GetArtistAlbums(ctx, req.ArtistId, limit, int(req.Offset), albumTypes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toGRPCError(ctx, err)
|
return nil, toGRPCError(ctx, err)
|
||||||
}
|
}
|
||||||
@@ -268,6 +270,15 @@ func (s *MetadataServer) SyncArtist(ctx context.Context, req *metadatav1.SyncArt
|
|||||||
return nil, status.Error(codes.Unimplemented, "sync not yet implemented")
|
return nil, status.Error(codes.Unimplemented, "sync not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultTypes = []string{"album", "ep", "single"}
|
||||||
|
|
||||||
|
func defaultAlbumTypes(types []string) []string {
|
||||||
|
if len(types) > 0 {
|
||||||
|
return types
|
||||||
|
}
|
||||||
|
return defaultTypes
|
||||||
|
}
|
||||||
|
|
||||||
func toGRPCError(ctx context.Context, err error) error {
|
func toGRPCError(ctx context.Context, err error) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ func (s *MetadataService) SearchArtists(ctx context.Context, query string, limit
|
|||||||
return s.provider.SearchArtists(ctx, query, limit, offset)
|
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) {
|
func (s *MetadataService) SearchAlbums(ctx context.Context, query string, artist string, limit, offset int, albumTypes []string) (*domain.SearchResult[domain.Album], error) {
|
||||||
zerolog.Ctx(ctx).Debug().Str("query", query).Str("artist", artist).Str("provider", s.provider.Name()).Msg("searching albums via provider")
|
zerolog.Ctx(ctx).Debug().Str("query", query).Str("artist", artist).Strs("album_types", albumTypes).Str("provider", s.provider.Name()).Msg("searching albums via provider")
|
||||||
return s.provider.SearchAlbums(ctx, query, artist, limit, offset)
|
return s.provider.SearchAlbums(ctx, query, artist, limit, offset, albumTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MetadataService) GetAlbum(ctx context.Context, id string) (*domain.Album, error) {
|
func (s *MetadataService) GetAlbum(ctx context.Context, id string) (*domain.Album, error) {
|
||||||
@@ -117,7 +117,7 @@ func (s *MetadataService) GetAlbum(ctx context.Context, id string) (*domain.Albu
|
|||||||
return album, nil
|
return album, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MetadataService) GetArtistAlbums(ctx context.Context, artistID string, limit, offset int) (*domain.SearchResult[domain.Album], error) {
|
func (s *MetadataService) GetArtistAlbums(ctx context.Context, artistID string, limit, offset int, albumTypes []string) (*domain.SearchResult[domain.Album], error) {
|
||||||
log := zerolog.Ctx(ctx)
|
log := zerolog.Ctx(ctx)
|
||||||
|
|
||||||
result, err := s.albums.GetByArtistID(ctx, artistID, limit, offset)
|
result, err := s.albums.GetByArtistID(ctx, artistID, limit, offset)
|
||||||
@@ -128,8 +128,8 @@ func (s *MetadataService) GetArtistAlbums(ctx context.Context, artistID string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
metrics.CacheMisses.WithLabelValues("artist_albums").Inc()
|
metrics.CacheMisses.WithLabelValues("artist_albums").Inc()
|
||||||
log.Debug().Str("artist_id", artistID).Str("provider", s.provider.Name()).Msg("artist albums cache miss, querying provider")
|
log.Debug().Str("artist_id", artistID).Strs("album_types", albumTypes).Str("provider", s.provider.Name()).Msg("artist albums cache miss, querying provider")
|
||||||
return s.provider.GetArtistAlbums(ctx, artistID, limit, offset)
|
return s.provider.GetArtistAlbums(ctx, artistID, limit, offset, albumTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MetadataService) GetTrack(ctx context.Context, id string) (*domain.Track, error) {
|
func (s *MetadataService) GetTrack(ctx context.Context, id string) (*domain.Track, error) {
|
||||||
|
|||||||
@@ -321,6 +321,7 @@ type GetArtistAlbumsRequest struct {
|
|||||||
Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
|
Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||||
Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"`
|
Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"`
|
||||||
Provider Provider `protobuf:"varint,4,opt,name=provider,proto3,enum=metadata.v1.Provider" json:"provider,omitempty"`
|
Provider Provider `protobuf:"varint,4,opt,name=provider,proto3,enum=metadata.v1.Provider" json:"provider,omitempty"`
|
||||||
|
AlbumTypes []string `protobuf:"bytes,5,rep,name=album_types,json=albumTypes,proto3" json:"album_types,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -383,6 +384,13 @@ func (x *GetArtistAlbumsRequest) GetProvider() Provider {
|
|||||||
return Provider_PROVIDER_UNSPECIFIED
|
return Provider_PROVIDER_UNSPECIFIED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *GetArtistAlbumsRequest) GetAlbumTypes() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.AlbumTypes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type GetTrackRequest struct {
|
type GetTrackRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
// Types that are valid to be assigned to Identifier:
|
// Types that are valid to be assigned to Identifier:
|
||||||
@@ -548,6 +556,7 @@ type SearchAlbumsRequest struct {
|
|||||||
Limit int32 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,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"`
|
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"`
|
Provider Provider `protobuf:"varint,5,opt,name=provider,proto3,enum=metadata.v1.Provider" json:"provider,omitempty"`
|
||||||
|
AlbumTypes []string `protobuf:"bytes,6,rep,name=album_types,json=albumTypes,proto3" json:"album_types,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -617,6 +626,13 @@ func (x *SearchAlbumsRequest) GetProvider() Provider {
|
|||||||
return Provider_PROVIDER_UNSPECIFIED
|
return Provider_PROVIDER_UNSPECIFIED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *SearchAlbumsRequest) GetAlbumTypes() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.AlbumTypes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type SyncArtistRequest struct {
|
type SyncArtistRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
// Types that are valid to be assigned to Target:
|
// Types that are valid to be assigned to Target:
|
||||||
@@ -1808,12 +1824,14 @@ const file_metadata_v1_metadata_proto_rawDesc = "" +
|
|||||||
"\bexternal\x18\x02 \x01(\v2\x17.metadata.v1.ExternalIDH\x00R\bexternal\x121\n" +
|
"\bexternal\x18\x02 \x01(\v2\x17.metadata.v1.ExternalIDH\x00R\bexternal\x121\n" +
|
||||||
"\bprovider\x18\x03 \x01(\x0e2\x15.metadata.v1.ProviderR\bproviderB\f\n" +
|
"\bprovider\x18\x03 \x01(\x0e2\x15.metadata.v1.ProviderR\bproviderB\f\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"identifier\"\x96\x01\n" +
|
"identifier\"\xb7\x01\n" +
|
||||||
"\x16GetArtistAlbumsRequest\x12\x1b\n" +
|
"\x16GetArtistAlbumsRequest\x12\x1b\n" +
|
||||||
"\tartist_id\x18\x01 \x01(\tR\bartistId\x12\x14\n" +
|
"\tartist_id\x18\x01 \x01(\tR\bartistId\x12\x14\n" +
|
||||||
"\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x16\n" +
|
"\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x16\n" +
|
||||||
"\x06offset\x18\x03 \x01(\x05R\x06offset\x121\n" +
|
"\x06offset\x18\x03 \x01(\x05R\x06offset\x121\n" +
|
||||||
"\bprovider\x18\x04 \x01(\x0e2\x15.metadata.v1.ProviderR\bprovider\"\xb1\x01\n" +
|
"\bprovider\x18\x04 \x01(\x0e2\x15.metadata.v1.ProviderR\bprovider\x12\x1f\n" +
|
||||||
|
"\valbum_types\x18\x05 \x03(\tR\n" +
|
||||||
|
"albumTypes\"\xb1\x01\n" +
|
||||||
"\x0fGetTrackRequest\x12\x10\n" +
|
"\x0fGetTrackRequest\x12\x10\n" +
|
||||||
"\x02id\x18\x01 \x01(\tH\x00R\x02id\x125\n" +
|
"\x02id\x18\x01 \x01(\tH\x00R\x02id\x125\n" +
|
||||||
"\bexternal\x18\x02 \x01(\v2\x17.metadata.v1.ExternalIDH\x00R\bexternal\x12\x14\n" +
|
"\bexternal\x18\x02 \x01(\v2\x17.metadata.v1.ExternalIDH\x00R\bexternal\x12\x14\n" +
|
||||||
@@ -1823,13 +1841,15 @@ const file_metadata_v1_metadata_proto_rawDesc = "" +
|
|||||||
"identifier\"e\n" +
|
"identifier\"e\n" +
|
||||||
"\x15GetAlbumTracksRequest\x12\x19\n" +
|
"\x15GetAlbumTracksRequest\x12\x19\n" +
|
||||||
"\balbum_id\x18\x01 \x01(\tR\aalbumId\x121\n" +
|
"\balbum_id\x18\x01 \x01(\tR\aalbumId\x121\n" +
|
||||||
"\bprovider\x18\x02 \x01(\x0e2\x15.metadata.v1.ProviderR\bprovider\"\xa4\x01\n" +
|
"\bprovider\x18\x02 \x01(\x0e2\x15.metadata.v1.ProviderR\bprovider\"\xc5\x01\n" +
|
||||||
"\x13SearchAlbumsRequest\x12\x14\n" +
|
"\x13SearchAlbumsRequest\x12\x14\n" +
|
||||||
"\x05query\x18\x01 \x01(\tR\x05query\x12\x16\n" +
|
"\x05query\x18\x01 \x01(\tR\x05query\x12\x16\n" +
|
||||||
"\x06artist\x18\x02 \x01(\tR\x06artist\x12\x14\n" +
|
"\x06artist\x18\x02 \x01(\tR\x06artist\x12\x14\n" +
|
||||||
"\x05limit\x18\x03 \x01(\x05R\x05limit\x12\x16\n" +
|
"\x05limit\x18\x03 \x01(\x05R\x05limit\x12\x16\n" +
|
||||||
"\x06offset\x18\x04 \x01(\x05R\x06offset\x121\n" +
|
"\x06offset\x18\x04 \x01(\x05R\x06offset\x121\n" +
|
||||||
"\bprovider\x18\x05 \x01(\x0e2\x15.metadata.v1.ProviderR\bprovider\"\x9d\x01\n" +
|
"\bprovider\x18\x05 \x01(\x0e2\x15.metadata.v1.ProviderR\bprovider\x12\x1f\n" +
|
||||||
|
"\valbum_types\x18\x06 \x03(\tR\n" +
|
||||||
|
"albumTypes\"\x9d\x01\n" +
|
||||||
"\x11SyncArtistRequest\x12\x14\n" +
|
"\x11SyncArtistRequest\x12\x14\n" +
|
||||||
"\x04name\x18\x01 \x01(\tH\x00R\x04name\x125\n" +
|
"\x04name\x18\x01 \x01(\tH\x00R\x04name\x125\n" +
|
||||||
"\bexternal\x18\x02 \x01(\v2\x17.metadata.v1.ExternalIDH\x00R\bexternal\x121\n" +
|
"\bexternal\x18\x02 \x01(\v2\x17.metadata.v1.ExternalIDH\x00R\bexternal\x121\n" +
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ message GetArtistAlbumsRequest {
|
|||||||
int32 limit = 2;
|
int32 limit = 2;
|
||||||
int32 offset = 3;
|
int32 offset = 3;
|
||||||
Provider provider = 4;
|
Provider provider = 4;
|
||||||
|
repeated string album_types = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetTrackRequest {
|
message GetTrackRequest {
|
||||||
@@ -72,6 +73,7 @@ message SearchAlbumsRequest {
|
|||||||
int32 limit = 3;
|
int32 limit = 3;
|
||||||
int32 offset = 4;
|
int32 offset = 4;
|
||||||
Provider provider = 5;
|
Provider provider = 5;
|
||||||
|
repeated string album_types = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SyncArtistRequest {
|
message SyncArtistRequest {
|
||||||
|
|||||||
Reference in New Issue
Block a user