Something + add comments to proto
This commit is contained in:
@@ -1 +1,8 @@
|
||||
use flake
|
||||
#!/usr/bin/env bash
|
||||
export GIT_CONFIG_GLOBAL=/dev/null
|
||||
|
||||
eval "$(devenv direnvrc)"
|
||||
|
||||
# You can pass flags to the devenv command
|
||||
# For example: use devenv --impure --option services.postgres.enable:bool true
|
||||
use devenv
|
||||
|
||||
@@ -12,3 +12,8 @@ vendor/
|
||||
|
||||
docs/research/*/repo/
|
||||
docs/research/*/repo-index/
|
||||
|
||||
# Devenv
|
||||
.devenv*
|
||||
devenv.local.nix
|
||||
devenv.local.yaml
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
info:
|
||||
name: SearchArtist
|
||||
type: grpc
|
||||
seq: 1
|
||||
|
||||
grpc:
|
||||
url: localhost:50053
|
||||
method: /metadata.v1.MetadataService/SearchArtists
|
||||
methodType: unary
|
||||
message: |-
|
||||
{
|
||||
"query": "ДДТ",
|
||||
"limit": 10,
|
||||
"offset": 0,
|
||||
"provider": 0
|
||||
}
|
||||
auth: inherit
|
||||
@@ -0,0 +1,25 @@
|
||||
opencollection: 1.0.0
|
||||
|
||||
info:
|
||||
name: Metadata Agregator
|
||||
config:
|
||||
proxy:
|
||||
inherit: true
|
||||
config:
|
||||
protocol: http
|
||||
hostname: ""
|
||||
port: ""
|
||||
auth:
|
||||
username: ""
|
||||
password: ""
|
||||
bypassProxy: ""
|
||||
bundled: false
|
||||
extensions:
|
||||
bruno:
|
||||
ignore:
|
||||
- node_modules
|
||||
- .git
|
||||
presets:
|
||||
request:
|
||||
type: grpc
|
||||
url: localhost:50053
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"locked": {
|
||||
"dir": "src/modules",
|
||||
"lastModified": 1783461239,
|
||||
"narHash": "sha256-O+rwg3jreOAOrYd0YrNoOsRu+8is6TCPCIBN3qJTJpM=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "6222c499fe28b87665af2bb6c626da64179cee00",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "src/modules",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1783008725,
|
||||
"narHash": "sha256-jGiy6+sxjNWXSjp25uoJuNfyH9zBK1PEDY0lVoL4ibQ=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "bca82caa46d5ec0f5d422c61fb1e30bc51313cbe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"inputs": {
|
||||
"nixpkgs-src": "nixpkgs-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1783345554,
|
||||
"narHash": "sha256-LZhOm4kqjHUnwP4CNHpaqqEVcumGNd1PvOEOad8LkS0=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "6004ea8c229fe9d41b21c6f4c76bf6c2e10771dd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "rolling",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1783279667,
|
||||
"narHash": "sha256-/NAkDSsve+GNM0Bt6tleJdCGfsTlK89nPjkVOzZMo0s=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f205b5574fd0cb7da5b702a2da51507b7f4fdd1b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"git-hooks": "git-hooks",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1780220602,
|
||||
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
languages.go.enable = true;
|
||||
|
||||
git-hooks.hooks = {
|
||||
treefmt.enable = true;
|
||||
};
|
||||
|
||||
treefmt = {
|
||||
enable = true;
|
||||
config.programs = {
|
||||
nixfmt.enable = true;
|
||||
gofmt.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
packages = with pkgs; [
|
||||
pre-commit
|
||||
gitleaks
|
||||
plantuml
|
||||
|
||||
gopls
|
||||
gotools
|
||||
go-tools
|
||||
golangci-lint
|
||||
|
||||
buf
|
||||
protoc-gen-go
|
||||
protoc-gen-go-grpc
|
||||
|
||||
grpcurl
|
||||
|
||||
opencode
|
||||
];
|
||||
|
||||
services.postgres = {
|
||||
enable = true;
|
||||
listen_addresses = "localhost";
|
||||
|
||||
initialDatabases = [
|
||||
{
|
||||
name = "metadata_agregator";
|
||||
schema = ./nix/schema/001_schema.sql;
|
||||
}
|
||||
];
|
||||
|
||||
initialScript = ''
|
||||
CREATE USER agregator WITH PASSWORD 'agregator';
|
||||
GRANT ALL PRIVILEGES ON DATABASE metadata_agregator TO agregator;
|
||||
\c metadata_agregator
|
||||
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO agregator;
|
||||
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO agregator;
|
||||
GRANT ALL ON SCHEMA public TO agregator;
|
||||
'';
|
||||
};
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
inputs:
|
||||
git-hooks:
|
||||
url: github:cachix/git-hooks.nix
|
||||
inputs:
|
||||
nixpkgs:
|
||||
follows: nixpkgs
|
||||
nixpkgs:
|
||||
url: github:cachix/devenv-nixpkgs/rolling
|
||||
treefmt-nix:
|
||||
url: github:numtide/treefmt-nix
|
||||
inputs:
|
||||
nixpkgs:
|
||||
follows: nixpkgs
|
||||
Generated
-134
@@ -1,134 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775087534,
|
||||
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776796298,
|
||||
"narHash": "sha256-PcRvlWayisPSjd0UcRQbhG8Oqw78AcPE6x872cPRHN8=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "3cfd774b0a530725a077e17354fbdb87ea1c4aad",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1770073757,
|
||||
"narHash": "sha256-Vy+G+F+3E/Tl+GMNgiHl9Pah2DgShmIUBJXmbiQPHbI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "47472570b1e607482890801aeaf29bfb749884f6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1774748309,
|
||||
"narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "333c4e0545a6da976206c74db8773a1645b5870a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1777326013,
|
||||
"narHash": "sha256-Z+out+ntVqKHsoZdmEM30+84A/and97fv5vouJtev1Q=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "52ffa3b778ab50cc305ff8927aca2b71a0ff50de",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"git-hooks": "git-hooks",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
git-hooks.url = "github:cachix/git-hooks.nix";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-parts,
|
||||
git-hooks,
|
||||
...
|
||||
}@inputs:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
];
|
||||
|
||||
flake = {
|
||||
nixosModules = {
|
||||
metadata-agregator = import ./nix/module.nix;
|
||||
default = self.nixosModules.metadata-agregator;
|
||||
};
|
||||
};
|
||||
|
||||
perSystem =
|
||||
{
|
||||
system,
|
||||
...
|
||||
}:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
|
||||
pre-commit-check = git-hooks.lib.${system}.run {
|
||||
src = ./.;
|
||||
hooks = {
|
||||
nixfmt.enable = true;
|
||||
gofmt.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
metadata-server = pkgs.buildGoModule {
|
||||
pname = "metadata-server";
|
||||
version = "0.1.0";
|
||||
src = ./.;
|
||||
vendorHash = "sha256-qNNFHRKqGoo/GveSl9+Bm+DkTKDoSQeGtG8p1edmCJk=";
|
||||
subPackages = [ "cmd/server" ];
|
||||
|
||||
meta = {
|
||||
description = "Music metadata aggregation gRPC service";
|
||||
mainProgram = "server";
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
formatter = pkgs.nixfmt-tree;
|
||||
|
||||
packages = {
|
||||
default = metadata-server;
|
||||
server = metadata-server;
|
||||
};
|
||||
|
||||
checks = {
|
||||
inherit pre-commit-check;
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
inherit (pre-commit-check) shellHook;
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
pre-commit
|
||||
gitleaks
|
||||
plantuml
|
||||
|
||||
go
|
||||
gopls
|
||||
gotools
|
||||
go-tools
|
||||
golangci-lint
|
||||
|
||||
buf
|
||||
protoc-gen-go
|
||||
protoc-gen-go-grpc
|
||||
|
||||
grpcurl
|
||||
|
||||
opencode
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -17,8 +17,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
baseURL = "https://musicbrainz.org/ws/2"
|
||||
userAgent = "MetadataAggregator/0.1.0 (https://github.com/metadata-agregator)"
|
||||
baseURL = "https://musicbrainz.org/ws/2"
|
||||
userAgent = "MetadataAggregator/0.1.0 (https://github.com/metadata-agregator)"
|
||||
maxRetries = 3
|
||||
baseBackoff = 2 * time.Second
|
||||
)
|
||||
|
||||
type client struct {
|
||||
@@ -36,16 +38,45 @@ func newClient() *client {
|
||||
}
|
||||
|
||||
func (c *client) get(ctx context.Context, endpoint string, params url.Values) ([]byte, error) {
|
||||
if err := c.waitForRateLimit(ctx); err != nil {
|
||||
return nil, err
|
||||
var lastErr error
|
||||
|
||||
for attempt := 0; attempt <= maxRetries; attempt++ {
|
||||
if err := c.waitForRateLimit(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := c.buildRequest(ctx, endpoint, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := c.executeAndRead(ctx, req, endpoint)
|
||||
if err == nil {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if err != ErrRateLimited {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lastErr = err
|
||||
if attempt < maxRetries {
|
||||
backoff := baseBackoff * time.Duration(1<<attempt)
|
||||
zerolog.Ctx(ctx).Info().
|
||||
Int("attempt", attempt+1).
|
||||
Int("max_retries", maxRetries).
|
||||
Dur("backoff", backoff).
|
||||
Msg("rate limited, waiting before retry")
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-time.After(backoff):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req, err := c.buildRequest(ctx, endpoint, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.executeAndRead(ctx, req, endpoint)
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
func (c *client) waitForRateLimit(ctx context.Context) error {
|
||||
|
||||
@@ -128,7 +128,7 @@ func (s *MetadataService) deduplicateArtists(dbResult, mbResult *domain.SearchRe
|
||||
total := 0
|
||||
|
||||
addArtist := func(artist domain.Artist) {
|
||||
if artist.Type == "Other" {
|
||||
if artist.Type == "" || artist.Type == "Other" {
|
||||
return
|
||||
}
|
||||
for _, ext := range artist.ExternalIDs {
|
||||
|
||||
@@ -4,8 +4,12 @@ package metadata.v1;
|
||||
|
||||
option go_package = "github.com/metadata-agregator/pkg/gen/metadata/v1;metadatav1";
|
||||
|
||||
// Provider identifies the upstream metadata source a request is resolved
|
||||
// against. It also selects which ID namespace bare `id` fields refer to.
|
||||
enum Provider {
|
||||
// Unset. Requests default to the server's configured default provider.
|
||||
PROVIDER_UNSPECIFIED = 0;
|
||||
// MusicBrainz (https://musicbrainz.org). Native IDs are MBIDs (UUIDs).
|
||||
PROVIDER_MUSICBRAINZ = 1;
|
||||
}
|
||||
|
||||
@@ -22,179 +26,319 @@ service MetadataService {
|
||||
|
||||
// Requests
|
||||
|
||||
// GetArtistRequest looks up a single artist by one of its identifiers.
|
||||
message GetArtistRequest {
|
||||
// How to locate the artist. Exactly one field must be set.
|
||||
oneof identifier {
|
||||
// Provider-native identifier for the artist, in the ID namespace of
|
||||
// `provider`. For PROVIDER_MUSICBRAINZ this is the artist MBID.
|
||||
// Example: "5b11f4ce-a62d-471e-81fc-a69a8278c7da" (Radiohead).
|
||||
string id = 1;
|
||||
// Identifier from a specific external source, when the source differs
|
||||
// from `provider` or must be named explicitly.
|
||||
// Example: {source: "musicbrainz", source_id: "5b11f4ce-..."}.
|
||||
ExternalID external = 2;
|
||||
}
|
||||
// Source to resolve against and the ID namespace of `id`.
|
||||
// Example: PROVIDER_MUSICBRAINZ.
|
||||
Provider provider = 3;
|
||||
}
|
||||
|
||||
// SearchArtistsRequest performs a fuzzy, paginated artist search.
|
||||
message SearchArtistsRequest {
|
||||
// Free-text search terms, typically the artist name. Example: "radiohead".
|
||||
string query = 1;
|
||||
// Maximum number of results to return. Example: 25 (0 uses the default).
|
||||
int32 limit = 2;
|
||||
// Number of leading results to skip, for pagination. Example: 25.
|
||||
int32 offset = 3;
|
||||
// Source to search. Example: PROVIDER_MUSICBRAINZ.
|
||||
Provider provider = 4;
|
||||
}
|
||||
|
||||
// GetAlbumRequest looks up a single album (release group) by identifier.
|
||||
message GetAlbumRequest {
|
||||
// How to locate the album. Exactly one field must be set.
|
||||
oneof identifier {
|
||||
// Provider-native album identifier, in the ID namespace of `provider`.
|
||||
// For PROVIDER_MUSICBRAINZ this is the release-group MBID.
|
||||
// Example: "b1392450-e666-3926-a536-22c65f834433" (OK Computer).
|
||||
string id = 1;
|
||||
// Identifier from a specific external source.
|
||||
// Example: {source: "musicbrainz", source_id: "b1392450-..."}.
|
||||
ExternalID external = 2;
|
||||
}
|
||||
// Source to resolve against. Example: PROVIDER_MUSICBRAINZ.
|
||||
Provider provider = 3;
|
||||
}
|
||||
|
||||
// GetArtistAlbumsRequest lists an artist's albums, paginated and optionally
|
||||
// filtered by release type.
|
||||
message GetArtistAlbumsRequest {
|
||||
// Provider-native artist identifier (same namespace as GetArtistRequest.id).
|
||||
// Example: "5b11f4ce-a62d-471e-81fc-a69a8278c7da".
|
||||
string artist_id = 1;
|
||||
// Maximum number of albums to return. Example: 50.
|
||||
int32 limit = 2;
|
||||
// Number of leading albums to skip, for pagination. Example: 0.
|
||||
int32 offset = 3;
|
||||
// Source to query. Example: PROVIDER_MUSICBRAINZ.
|
||||
Provider provider = 4;
|
||||
// Optional release-type filter; empty means all types.
|
||||
// Example: ["album", "ep"].
|
||||
repeated string album_types = 5;
|
||||
}
|
||||
|
||||
// GetTrackRequest looks up a single track (recording) by identifier.
|
||||
message GetTrackRequest {
|
||||
// How to locate the track. Exactly one field must be set.
|
||||
oneof identifier {
|
||||
// Provider-native track identifier, in the ID namespace of `provider`.
|
||||
// For PROVIDER_MUSICBRAINZ this is the recording MBID.
|
||||
// Example: "b9d99e0e-1472-4a37-8b2a-2f6b0a1234ab".
|
||||
string id = 1;
|
||||
// Identifier from a specific external source.
|
||||
// Example: {source: "musicbrainz", source_id: "b9d99e0e-..."}.
|
||||
ExternalID external = 2;
|
||||
// International Standard Recording Code. Example: "GBAYE9600477".
|
||||
string isrc = 3;
|
||||
}
|
||||
// Source to resolve against. Example: PROVIDER_MUSICBRAINZ.
|
||||
Provider provider = 4;
|
||||
}
|
||||
|
||||
// GetAlbumTracksRequest lists the tracks belonging to an album.
|
||||
message GetAlbumTracksRequest {
|
||||
// Provider-native album identifier (same namespace as GetAlbumRequest.id).
|
||||
// Example: "b1392450-e666-3926-a536-22c65f834433".
|
||||
string album_id = 1;
|
||||
// Source to query. Example: PROVIDER_MUSICBRAINZ.
|
||||
Provider provider = 2;
|
||||
}
|
||||
|
||||
// SearchAlbumsRequest performs a fuzzy, paginated album search.
|
||||
message SearchAlbumsRequest {
|
||||
// Free-text search terms, typically the album title. Example: "ok computer".
|
||||
string query = 1;
|
||||
// Optional artist name to narrow the search. Example: "radiohead".
|
||||
string artist = 2;
|
||||
// Maximum number of results to return. Example: 25.
|
||||
int32 limit = 3;
|
||||
// Number of leading results to skip, for pagination. Example: 0.
|
||||
int32 offset = 4;
|
||||
// Source to search. Example: PROVIDER_MUSICBRAINZ.
|
||||
Provider provider = 5;
|
||||
// Optional release-type filter; empty means all types.
|
||||
// Example: ["album"].
|
||||
repeated string album_types = 6;
|
||||
}
|
||||
|
||||
// SyncArtistRequest fetches an artist and their catalog from a provider and
|
||||
// persists it to the local store.
|
||||
message SyncArtistRequest {
|
||||
// How to identify the artist to sync. Exactly one field must be set.
|
||||
oneof target {
|
||||
// Artist name to resolve via search before syncing. Example: "Radiohead".
|
||||
string name = 1;
|
||||
// Exact external identifier of the artist to sync.
|
||||
// Example: {source: "musicbrainz", source_id: "5b11f4ce-..."}.
|
||||
ExternalID external = 2;
|
||||
}
|
||||
// Source to sync from. Example: PROVIDER_MUSICBRAINZ.
|
||||
Provider provider = 3;
|
||||
}
|
||||
|
||||
// Responses
|
||||
|
||||
message GetArtistResponse {
|
||||
// The resolved artist.
|
||||
Artist artist = 1;
|
||||
}
|
||||
|
||||
message SearchArtistsResponse {
|
||||
// Matching artists, ordered by relevance.
|
||||
repeated Artist artists = 1;
|
||||
// Total number of matches available across all pages. Example: 137.
|
||||
int32 total = 2;
|
||||
}
|
||||
|
||||
message GetAlbumResponse {
|
||||
// The resolved album.
|
||||
Album album = 1;
|
||||
}
|
||||
|
||||
message GetArtistAlbumsResponse {
|
||||
// The artist's albums for the requested page.
|
||||
repeated Album albums = 1;
|
||||
// Total number of albums available across all pages. Example: 42.
|
||||
int32 total = 2;
|
||||
}
|
||||
|
||||
message GetTrackResponse {
|
||||
// The resolved track.
|
||||
Track track = 1;
|
||||
}
|
||||
|
||||
message GetAlbumTracksResponse {
|
||||
// The album's tracks, ordered by disc then track number.
|
||||
repeated Track tracks = 1;
|
||||
}
|
||||
|
||||
message SearchAlbumsResponse {
|
||||
// Matching albums, ordered by relevance.
|
||||
repeated Album albums = 1;
|
||||
// Total number of matches available across all pages. Example: 58.
|
||||
int32 total = 2;
|
||||
}
|
||||
|
||||
message SyncArtistResponse {
|
||||
// The synced artist as persisted locally.
|
||||
Artist artist = 1;
|
||||
// Number of albums fetched and stored during the sync. Example: 12.
|
||||
int32 albums_synced = 2;
|
||||
// Number of tracks fetched and stored during the sync. Example: 148.
|
||||
int32 tracks_synced = 3;
|
||||
}
|
||||
|
||||
// Core Entities
|
||||
|
||||
// Artist is a performer or group.
|
||||
message Artist {
|
||||
// Provider-native identifier (MBID for MusicBrainz).
|
||||
// Example: "5b11f4ce-a62d-471e-81fc-a69a8278c7da".
|
||||
string id = 1;
|
||||
// Display name. Example: "Radiohead".
|
||||
string name = 2;
|
||||
// Name normalized for alphabetical sorting. Example: "Radiohead"; for a
|
||||
// person, "Beatles, The" or "Hendrix, Jimi".
|
||||
string sort_name = 3;
|
||||
// Kind of artist. Example: "Group", "Person".
|
||||
string artist_type = 4;
|
||||
// ISO 3166-1 alpha-2 country code. Example: "GB".
|
||||
string country = 5;
|
||||
// Date the artist formed or was born, ISO 8601. Partial dates allowed.
|
||||
// Example: "1985" or "1985-05-01".
|
||||
string formed_date = 6;
|
||||
// Date the artist disbanded or died, ISO 8601; empty if still active.
|
||||
// Example: "2011-04-30".
|
||||
string disbanded_date = 7;
|
||||
// Free-text biography or annotation.
|
||||
string description = 8;
|
||||
// URL of a representative image. Example: "https://example.org/radiohead.jpg".
|
||||
string image_url = 9;
|
||||
// Genres associated with the artist.
|
||||
repeated Genre genres = 10;
|
||||
// Identifiers for this artist in external sources.
|
||||
repeated ExternalID external_ids = 11;
|
||||
}
|
||||
|
||||
// Album is a release group / collection of tracks.
|
||||
message Album {
|
||||
// Provider-native identifier (release-group MBID for MusicBrainz).
|
||||
// Example: "b1392450-e666-3926-a536-22c65f834433".
|
||||
string id = 1;
|
||||
// Album title. Example: "OK Computer".
|
||||
string title = 2;
|
||||
// Release type. Example: "album", "ep", "single", "compilation".
|
||||
string album_type = 3;
|
||||
// Release date, ISO 8601. Partial dates allowed. Example: "1997-05-21".
|
||||
string release_date = 4;
|
||||
// Universal Product Code (barcode). Example: "724385522925".
|
||||
string upc = 5;
|
||||
// Number of tracks across all discs. Example: 12.
|
||||
int32 total_tracks = 6;
|
||||
// Number of discs in the release. Example: 1.
|
||||
int32 total_discs = 7;
|
||||
// URL of the cover art. Example: "https://coverartarchive.org/release/.../front".
|
||||
string cover_url = 8;
|
||||
// Credited artists, in credit order.
|
||||
repeated ArtistCredit artists = 9;
|
||||
// Releasing label, if known.
|
||||
Label label = 10;
|
||||
// Genres associated with the album.
|
||||
repeated Genre genres = 11;
|
||||
// Identifiers for this album in external sources.
|
||||
repeated ExternalID external_ids = 12;
|
||||
}
|
||||
|
||||
// Track is a recording as it appears on an album.
|
||||
message Track {
|
||||
// Provider-native identifier (recording MBID for MusicBrainz).
|
||||
// Example: "b9d99e0e-1472-4a37-8b2a-2f6b0a1234ab".
|
||||
string id = 1;
|
||||
// Track title. Example: "Paranoid Android".
|
||||
string title = 2;
|
||||
// Playing time in milliseconds. Example: 383000 (6:23).
|
||||
int32 duration_ms = 3;
|
||||
// International Standard Recording Code. Example: "GBAYE9600477".
|
||||
string isrc = 4;
|
||||
// Whether the track is flagged as explicit.
|
||||
bool explicit = 5;
|
||||
// 1-based disc number the track appears on. Example: 1.
|
||||
int32 disc_number = 6;
|
||||
// 1-based position of the track within its disc. Example: 2.
|
||||
int32 track_number = 7;
|
||||
// Credited performers, in credit order.
|
||||
repeated ArtistCredit artists = 8;
|
||||
// Underlying composition, if linked.
|
||||
Work work = 9;
|
||||
// Identifiers for this track in external sources.
|
||||
repeated ExternalID external_ids = 10;
|
||||
}
|
||||
|
||||
// Work is the underlying composition a track recording is based on.
|
||||
message Work {
|
||||
// Provider-native identifier (work MBID for MusicBrainz).
|
||||
string id = 1;
|
||||
// Work title. Example: "Paranoid Android".
|
||||
string title = 2;
|
||||
// Kind of work. Example: "Song".
|
||||
string work_type = 3;
|
||||
// ISO 639-3 language code of the lyrics. Example: "eng".
|
||||
string language = 4;
|
||||
// Credited composers and lyricists.
|
||||
repeated ArtistCredit composers = 5;
|
||||
}
|
||||
|
||||
// Label is a record label.
|
||||
message Label {
|
||||
// Provider-native identifier (label MBID for MusicBrainz).
|
||||
string id = 1;
|
||||
// Label name. Example: "Parlophone".
|
||||
string name = 2;
|
||||
// ISO 3166-1 alpha-2 country code. Example: "GB".
|
||||
string country = 3;
|
||||
}
|
||||
|
||||
// Genre is a musical genre tag.
|
||||
message Genre {
|
||||
// Provider-native identifier (genre MBID for MusicBrainz).
|
||||
string id = 1;
|
||||
// Genre name. Example: "alternative rock".
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
// ArtistCredit links an artist to a release or track, preserving how the
|
||||
// artist is credited (role, order, and connecting text).
|
||||
message ArtistCredit {
|
||||
// The credited artist.
|
||||
Artist artist = 1;
|
||||
// Role in this credit. Example: "main", "featured", "composer".
|
||||
string role = 2;
|
||||
// 0-based position within the credit list. Example: 0.
|
||||
int32 position = 3;
|
||||
// Text joining this credit to the next. Example: " feat. " or " & ".
|
||||
string join_phrase = 4;
|
||||
}
|
||||
|
||||
// ExternalID identifies an entity within a third-party source.
|
||||
message ExternalID {
|
||||
// Source name. Example: "musicbrainz", "spotify", "discogs".
|
||||
string source = 1;
|
||||
// The entity's identifier within that source.
|
||||
// Example: "5b11f4ce-a62d-471e-81fc-a69a8278c7da".
|
||||
string source_id = 2;
|
||||
// Canonical URL of the entity at the source, if available.
|
||||
// Example: "https://musicbrainz.org/artist/5b11f4ce-...".
|
||||
string url = 3;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user