Add GetArtists RPC with artist monitor state (monitored/unmonitored/excluded)
This commit is contained in:
@@ -1,225 +0,0 @@
|
|||||||
-- Music Aggregator Database Schema
|
|
||||||
-- Based on docs/erd.puml
|
|
||||||
|
|
||||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
||||||
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
-- CONFIGURATION
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
CREATE TABLE quality_profiles (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
name TEXT NOT NULL UNIQUE,
|
|
||||||
cutoff INT NOT NULL DEFAULT 0,
|
|
||||||
items JSONB NOT NULL DEFAULT '[]',
|
|
||||||
upgrade_allowed BOOLEAN NOT NULL DEFAULT true
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE metadata_profiles (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
name TEXT NOT NULL UNIQUE,
|
|
||||||
primary_album_types JSONB NOT NULL DEFAULT '["Album", "EP"]',
|
|
||||||
secondary_album_types JSONB NOT NULL DEFAULT '[]',
|
|
||||||
release_statuses JSONB NOT NULL DEFAULT '["Official"]'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE root_folders (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
path TEXT NOT NULL UNIQUE,
|
|
||||||
default_quality_profile_id UUID REFERENCES quality_profiles(id),
|
|
||||||
default_metadata_profile_id UUID REFERENCES metadata_profiles(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE indexers (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
implementation TEXT NOT NULL,
|
|
||||||
settings JSONB NOT NULL DEFAULT '{}',
|
|
||||||
enable_rss BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
enable_search BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
priority INT NOT NULL DEFAULT 25
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE download_clients (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
implementation TEXT NOT NULL,
|
|
||||||
settings JSONB NOT NULL DEFAULT '{}',
|
|
||||||
protocol TEXT NOT NULL DEFAULT 'torrent',
|
|
||||||
priority INT NOT NULL DEFAULT 1,
|
|
||||||
enabled BOOLEAN NOT NULL DEFAULT true
|
|
||||||
);
|
|
||||||
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
-- CORE MUSIC ENTITIES
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
CREATE TABLE artist_metadata (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
foreign_artist_id TEXT UNIQUE,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
sort_name TEXT,
|
|
||||||
disambiguation TEXT,
|
|
||||||
artist_type TEXT,
|
|
||||||
status TEXT,
|
|
||||||
overview TEXT,
|
|
||||||
images JSONB NOT NULL DEFAULT '[]',
|
|
||||||
links JSONB NOT NULL DEFAULT '[]',
|
|
||||||
genres JSONB NOT NULL DEFAULT '[]',
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE artists (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
metadata_id UUID NOT NULL REFERENCES artist_metadata(id) ON DELETE CASCADE,
|
|
||||||
quality_profile_id UUID REFERENCES quality_profiles(id),
|
|
||||||
metadata_profile_id UUID REFERENCES metadata_profiles(id),
|
|
||||||
root_folder_id UUID REFERENCES root_folders(id),
|
|
||||||
path TEXT,
|
|
||||||
monitored BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
monitor_new_items TEXT NOT NULL DEFAULT 'all',
|
|
||||||
last_info_sync TIMESTAMPTZ,
|
|
||||||
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE albums (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
artist_metadata_id UUID NOT NULL REFERENCES artist_metadata(id) ON DELETE CASCADE,
|
|
||||||
foreign_album_id TEXT UNIQUE,
|
|
||||||
title TEXT NOT NULL,
|
|
||||||
clean_title TEXT,
|
|
||||||
disambiguation TEXT,
|
|
||||||
overview TEXT,
|
|
||||||
album_type TEXT,
|
|
||||||
release_date DATE,
|
|
||||||
images JSONB NOT NULL DEFAULT '[]',
|
|
||||||
genres JSONB NOT NULL DEFAULT '[]',
|
|
||||||
monitored BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE album_releases (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
|
|
||||||
foreign_release_id TEXT UNIQUE,
|
|
||||||
title TEXT NOT NULL,
|
|
||||||
status TEXT,
|
|
||||||
duration_ms INT,
|
|
||||||
release_date DATE,
|
|
||||||
country TEXT[],
|
|
||||||
label TEXT[],
|
|
||||||
format TEXT,
|
|
||||||
track_count INT,
|
|
||||||
monitored BOOLEAN NOT NULL DEFAULT true
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE track_files (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
|
|
||||||
path TEXT NOT NULL,
|
|
||||||
relative_path TEXT NOT NULL,
|
|
||||||
size BIGINT NOT NULL DEFAULT 0,
|
|
||||||
file_hash TEXT,
|
|
||||||
audio_hash TEXT,
|
|
||||||
quality JSONB NOT NULL DEFAULT '{}',
|
|
||||||
media_info JSONB NOT NULL DEFAULT '{}',
|
|
||||||
scene_name TEXT,
|
|
||||||
release_group TEXT,
|
|
||||||
date_added TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE tracks (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
album_release_id UUID NOT NULL REFERENCES album_releases(id) ON DELETE CASCADE,
|
|
||||||
artist_metadata_id UUID NOT NULL REFERENCES artist_metadata(id) ON DELETE CASCADE,
|
|
||||||
track_file_id UUID REFERENCES track_files(id) ON DELETE SET NULL,
|
|
||||||
foreign_track_id TEXT UNIQUE,
|
|
||||||
title TEXT NOT NULL,
|
|
||||||
track_number INT NOT NULL DEFAULT 1,
|
|
||||||
disc_number INT NOT NULL DEFAULT 1,
|
|
||||||
duration_ms INT,
|
|
||||||
explicit BOOLEAN NOT NULL DEFAULT false
|
|
||||||
);
|
|
||||||
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
-- DOWNLOAD TRACKING
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
CREATE TABLE wanted_albums (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
album_id UUID NOT NULL UNIQUE REFERENCES albums(id) ON DELETE CASCADE,
|
|
||||||
priority INT NOT NULL DEFAULT 0,
|
|
||||||
search_count INT NOT NULL DEFAULT 0,
|
|
||||||
last_searched_at TIMESTAMPTZ,
|
|
||||||
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE download_queue (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
artist_id UUID REFERENCES artists(id) ON DELETE SET NULL,
|
|
||||||
album_id UUID REFERENCES albums(id) ON DELETE SET NULL,
|
|
||||||
download_id TEXT,
|
|
||||||
title TEXT NOT NULL,
|
|
||||||
size BIGINT NOT NULL DEFAULT 0,
|
|
||||||
size_left BIGINT NOT NULL DEFAULT 0,
|
|
||||||
status TEXT NOT NULL DEFAULT 'queued',
|
|
||||||
progress REAL NOT NULL DEFAULT 0.0,
|
|
||||||
error_message TEXT,
|
|
||||||
protocol TEXT NOT NULL DEFAULT 'torrent',
|
|
||||||
indexer TEXT,
|
|
||||||
download_client TEXT,
|
|
||||||
torrent_hash TEXT,
|
|
||||||
output_path TEXT,
|
|
||||||
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
completed_at TIMESTAMPTZ
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE blocklist (
|
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
artist_id UUID NOT NULL REFERENCES artists(id) ON DELETE CASCADE,
|
|
||||||
album_id UUID REFERENCES albums(id) ON DELETE CASCADE,
|
|
||||||
source_title TEXT NOT NULL,
|
|
||||||
quality JSONB NOT NULL DEFAULT '{}',
|
|
||||||
size BIGINT NOT NULL DEFAULT 0,
|
|
||||||
protocol TEXT,
|
|
||||||
indexer TEXT,
|
|
||||||
message TEXT,
|
|
||||||
torrent_hash TEXT,
|
|
||||||
date TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
-- INDEXES
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
CREATE INDEX idx_artist_metadata_name ON artist_metadata(name);
|
|
||||||
CREATE INDEX idx_artist_metadata_foreign_id ON artist_metadata(foreign_artist_id);
|
|
||||||
CREATE INDEX idx_albums_artist ON albums(artist_metadata_id);
|
|
||||||
CREATE INDEX idx_albums_foreign_id ON albums(foreign_album_id);
|
|
||||||
CREATE INDEX idx_albums_release_date ON albums(release_date);
|
|
||||||
CREATE INDEX idx_album_releases_album ON album_releases(album_id);
|
|
||||||
CREATE INDEX idx_tracks_release ON tracks(album_release_id);
|
|
||||||
CREATE INDEX idx_tracks_artist ON tracks(artist_metadata_id);
|
|
||||||
CREATE INDEX idx_track_files_album ON track_files(album_id);
|
|
||||||
CREATE INDEX idx_track_files_hash ON track_files(file_hash);
|
|
||||||
CREATE INDEX idx_track_files_audio_hash ON track_files(audio_hash);
|
|
||||||
CREATE INDEX idx_wanted_albums_priority ON wanted_albums(priority DESC);
|
|
||||||
CREATE INDEX idx_download_queue_status ON download_queue(status);
|
|
||||||
CREATE INDEX idx_download_queue_album ON download_queue(album_id);
|
|
||||||
CREATE INDEX idx_blocklist_artist ON blocklist(artist_id);
|
|
||||||
CREATE INDEX idx_blocklist_torrent ON blocklist(torrent_hash);
|
|
||||||
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
-- DEFAULT DATA
|
|
||||||
-- ══════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
INSERT INTO quality_profiles (name, cutoff, items, upgrade_allowed) VALUES
|
|
||||||
('Any', 0, '[]', true),
|
|
||||||
('Lossless', 1, '[{"quality": "FLAC", "allowed": true}, {"quality": "ALAC", "allowed": true}]', true),
|
|
||||||
('Standard', 2, '[{"quality": "MP3-320", "allowed": true}, {"quality": "MP3-VBR-V0", "allowed": true}]', true);
|
|
||||||
|
|
||||||
INSERT INTO metadata_profiles (name, primary_album_types, secondary_album_types, release_statuses) VALUES
|
|
||||||
('Standard', '["Album", "EP"]', '[]', '["Official"]'),
|
|
||||||
('All', '["Album", "EP", "Single", "Broadcast", "Other"]', '["Compilation", "Soundtrack", "Spokenword", "Interview", "Audiobook", "Live", "Remix", "DJ-mix", "Mixtape/Street", "Demo"]', '["Official", "Promotional", "Bootleg"]');
|
|
||||||
@@ -8,16 +8,25 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ArtistMonitorState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ArtistMonitored ArtistMonitorState = "monitored"
|
||||||
|
ArtistUnmonitored ArtistMonitorState = "unmonitored"
|
||||||
|
ArtistExcluded ArtistMonitorState = "excluded"
|
||||||
|
)
|
||||||
|
|
||||||
type Artist struct {
|
type Artist struct {
|
||||||
ID string
|
ID string
|
||||||
ExternalID string
|
ExternalID string
|
||||||
Name string
|
Name string
|
||||||
ArtistType string
|
ArtistType string
|
||||||
Country string
|
Country string
|
||||||
Genres []string
|
Genres []string
|
||||||
ImageURL string
|
ImageURL string
|
||||||
CreatedAt time.Time
|
MonitorState ArtistMonitorState
|
||||||
UpdatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArtistRepository struct {
|
type ArtistRepository struct {
|
||||||
@@ -30,17 +39,21 @@ func NewArtistRepository(pool *pgxpool.Pool) *ArtistRepository {
|
|||||||
|
|
||||||
func (r *ArtistRepository) Create(ctx context.Context, a *Artist) error {
|
func (r *ArtistRepository) Create(ctx context.Context, a *Artist) error {
|
||||||
_, err := r.pool.Exec(ctx,
|
_, err := r.pool.Exec(ctx,
|
||||||
`INSERT INTO artists (external_id, name, artist_type, country, genres, image_url)
|
`INSERT INTO artists (external_id, name, artist_type, country, genres, image_url, monitor_state)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
ON CONFLICT (external_id) DO UPDATE SET
|
ON CONFLICT (external_id) DO UPDATE SET
|
||||||
name = EXCLUDED.name,
|
name = EXCLUDED.name,
|
||||||
artist_type = EXCLUDED.artist_type,
|
artist_type = EXCLUDED.artist_type,
|
||||||
country = EXCLUDED.country,
|
country = EXCLUDED.country,
|
||||||
genres = EXCLUDED.genres,
|
genres = EXCLUDED.genres,
|
||||||
image_url = EXCLUDED.image_url,
|
image_url = EXCLUDED.image_url,
|
||||||
|
monitor_state = CASE
|
||||||
|
WHEN artists.monitor_state = 'excluded' THEN artists.monitor_state
|
||||||
|
ELSE EXCLUDED.monitor_state
|
||||||
|
END,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
RETURNING id, created_at, updated_at`,
|
RETURNING id, created_at, updated_at`,
|
||||||
a.ExternalID, a.Name, a.ArtistType, a.Country, a.Genres, a.ImageURL,
|
a.ExternalID, a.Name, a.ArtistType, a.Country, a.Genres, a.ImageURL, a.MonitorState,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating artist: %w", err)
|
return fmt.Errorf("creating artist: %w", err)
|
||||||
@@ -51,21 +64,42 @@ func (r *ArtistRepository) Create(ctx context.Context, a *Artist) error {
|
|||||||
func (r *ArtistRepository) GetByExternalID(ctx context.Context, externalID string) (*Artist, error) {
|
func (r *ArtistRepository) GetByExternalID(ctx context.Context, externalID string) (*Artist, error) {
|
||||||
a := &Artist{}
|
a := &Artist{}
|
||||||
err := r.pool.QueryRow(ctx,
|
err := r.pool.QueryRow(ctx,
|
||||||
`SELECT id, external_id, name, artist_type, country, genres, image_url, created_at, updated_at
|
`SELECT id, external_id, name, artist_type, country, genres, image_url, monitor_state, created_at, updated_at
|
||||||
FROM artists WHERE external_id = $1`, externalID,
|
FROM artists WHERE external_id = $1`, externalID,
|
||||||
).Scan(&a.ID, &a.ExternalID, &a.Name, &a.ArtistType, &a.Country, &a.Genres, &a.ImageURL, &a.CreatedAt, &a.UpdatedAt)
|
).Scan(&a.ID, &a.ExternalID, &a.Name, &a.ArtistType, &a.Country, &a.Genres, &a.ImageURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getting artist: %w", err)
|
return nil, fmt.Errorf("getting artist: %w", err)
|
||||||
}
|
}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ArtistRepository) GetAll(ctx context.Context) ([]*Artist, error) {
|
||||||
|
rows, err := r.pool.Query(ctx,
|
||||||
|
`SELECT id, external_id, name, artist_type, country, genres, image_url, monitor_state, created_at, updated_at
|
||||||
|
FROM artists ORDER BY name ASC`,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listing artists: %w", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var artists []*Artist
|
||||||
|
for rows.Next() {
|
||||||
|
a := &Artist{}
|
||||||
|
if err := rows.Scan(&a.ID, &a.ExternalID, &a.Name, &a.ArtistType, &a.Country, &a.Genres, &a.ImageURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt); err != nil {
|
||||||
|
return nil, fmt.Errorf("scanning artist: %w", err)
|
||||||
|
}
|
||||||
|
artists = append(artists, a)
|
||||||
|
}
|
||||||
|
return artists, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ArtistRepository) GetByID(ctx context.Context, id string) (*Artist, error) {
|
func (r *ArtistRepository) GetByID(ctx context.Context, id string) (*Artist, error) {
|
||||||
a := &Artist{}
|
a := &Artist{}
|
||||||
err := r.pool.QueryRow(ctx,
|
err := r.pool.QueryRow(ctx,
|
||||||
`SELECT id, external_id, name, artist_type, country, genres, image_url, created_at, updated_at
|
`SELECT id, external_id, name, artist_type, country, genres, image_url, monitor_state, created_at, updated_at
|
||||||
FROM artists WHERE id = $1`, id,
|
FROM artists WHERE id = $1`, id,
|
||||||
).Scan(&a.ID, &a.ExternalID, &a.Name, &a.ArtistType, &a.Country, &a.Genres, &a.ImageURL, &a.CreatedAt, &a.UpdatedAt)
|
).Scan(&a.ID, &a.ExternalID, &a.Name, &a.ArtistType, &a.Country, &a.Genres, &a.ImageURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getting artist: %w", err)
|
return nil, fmt.Errorf("getting artist: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,12 +62,13 @@ func (s *MetadataService) persistArtist(ctx context.Context, album *metadataPb.A
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := s.artists.Create(ctx, &database.Artist{
|
err := s.artists.Create(ctx, &database.Artist{
|
||||||
ExternalID: artist.GetId(),
|
ExternalID: artist.GetId(),
|
||||||
Name: artist.GetName(),
|
Name: artist.GetName(),
|
||||||
ArtistType: artist.GetArtistType(),
|
ArtistType: artist.GetArtistType(),
|
||||||
Country: artist.GetCountry(),
|
Country: artist.GetCountry(),
|
||||||
Genres: genres,
|
Genres: genres,
|
||||||
ImageURL: artist.GetImageUrl(),
|
ImageURL: artist.GetImageUrl(),
|
||||||
|
MonitorState: database.ArtistMonitored,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Str("name", artist.GetName()).Msg("failed to persist artist")
|
log.Warn().Err(err).Str("name", artist.GetName()).Msg("failed to persist artist")
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ func NewMusicAgregatorServer(cfg config.Config, riverClient *river.Client[pgx.Tx
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MusicAgregatorServer) GetArtists(ctx context.Context, req *pb.GetArtistsRequest) (*pb.GetArtistsResponse, error) {
|
||||||
|
return s.service.GetArtists(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *MusicAgregatorServer) MonitorAlbum(ctx context.Context, req *pb.MonitorAlbumRequest) (*pb.MonitorAlbumResponse, error) {
|
func (s *MusicAgregatorServer) MonitorAlbum(ctx context.Context, req *pb.MonitorAlbumRequest) (*pb.MonitorAlbumResponse, error) {
|
||||||
return s.service.MonitorAlbum(ctx, req)
|
return s.service.MonitorAlbum(ctx, req)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ type MusicAgregatorService struct {
|
|||||||
riverClient *river.Client[pgx.Tx]
|
riverClient *river.Client[pgx.Tx]
|
||||||
torrents *database.TorrentRepository
|
torrents *database.TorrentRepository
|
||||||
downloads *database.DownloadRepository
|
downloads *database.DownloadRepository
|
||||||
|
artists *database.ArtistRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMusicAgregatorService(cfg config.Config, riverClient *river.Client[pgx.Tx], db *database.DB) (*MusicAgregatorService, error) {
|
func NewMusicAgregatorService(cfg config.Config, riverClient *river.Client[pgx.Tx], db *database.DB) (*MusicAgregatorService, error) {
|
||||||
@@ -76,6 +77,7 @@ func NewMusicAgregatorService(cfg config.Config, riverClient *river.Client[pgx.T
|
|||||||
riverClient: riverClient,
|
riverClient: riverClient,
|
||||||
torrents: database.NewTorrentRepository(db.Pool),
|
torrents: database.NewTorrentRepository(db.Pool),
|
||||||
downloads: database.NewDownloadRepository(db.Pool),
|
downloads: database.NewDownloadRepository(db.Pool),
|
||||||
|
artists: database.NewArtistRepository(db.Pool),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +87,30 @@ func (s *MusicAgregatorService) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service *MusicAgregatorService) GetArtists(ctx context.Context, _ *pb.GetArtistsRequest) (*pb.GetArtistsResponse, error) {
|
||||||
|
dbArtists, err := service.artists.GetAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("failed to list artists")
|
||||||
|
return nil, fmt.Errorf("listing artists: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
artists := make([]*pb.ArtistSummary, 0, len(dbArtists))
|
||||||
|
for _, a := range dbArtists {
|
||||||
|
artists = append(artists, &pb.ArtistSummary{
|
||||||
|
Id: a.ID,
|
||||||
|
ExternalId: a.ExternalID,
|
||||||
|
Name: a.Name,
|
||||||
|
ArtistType: a.ArtistType,
|
||||||
|
Country: a.Country,
|
||||||
|
Genres: a.Genres,
|
||||||
|
ImageUrl: a.ImageURL,
|
||||||
|
MonitorState: toProtoMonitorState(a.MonitorState),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.GetArtistsResponse{Artists: artists}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (service *MusicAgregatorService) MonitorAlbum(ctx context.Context, req *pb.MonitorAlbumRequest) (*pb.MonitorAlbumResponse, error) {
|
func (service *MusicAgregatorService) MonitorAlbum(ctx context.Context, req *pb.MonitorAlbumRequest) (*pb.MonitorAlbumResponse, error) {
|
||||||
album, err := service.metadata.GetAlbum(ctx, req.GetAlbumId())
|
album, err := service.metadata.GetAlbum(ctx, req.GetAlbumId())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -378,6 +404,19 @@ func buildMonitoredRelease(p parsedItem) *pb.MonitoredRelease {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toProtoMonitorState(state database.ArtistMonitorState) pb.ArtistMonitorState {
|
||||||
|
switch state {
|
||||||
|
case database.ArtistMonitored:
|
||||||
|
return pb.ArtistMonitorState_ARTIST_MONITOR_STATE_MONITORED
|
||||||
|
case database.ArtistUnmonitored:
|
||||||
|
return pb.ArtistMonitorState_ARTIST_MONITOR_STATE_UNMONITORED
|
||||||
|
case database.ArtistExcluded:
|
||||||
|
return pb.ArtistMonitorState_ARTIST_MONITOR_STATE_EXCLUDED
|
||||||
|
default:
|
||||||
|
return pb.ArtistMonitorState_ARTIST_MONITOR_STATE_UNSPECIFIED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func downloadTorrentData(url string) ([]byte, error) {
|
func downloadTorrentData(url string) ([]byte, error) {
|
||||||
client := &http.Client{Timeout: 30 * time.Second}
|
client := &http.Client{Timeout: 30 * time.Second}
|
||||||
resp, err := client.Get(url)
|
resp, err := client.Get(url)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ option go_package = "homelab.lan/music-agregator/gen/music_agregator/v1/";
|
|||||||
|
|
||||||
service MusicAgregatorService {
|
service MusicAgregatorService {
|
||||||
rpc MonitorAlbum(MonitorAlbumRequest) returns (MonitorAlbumResponse) {}
|
rpc MonitorAlbum(MonitorAlbumRequest) returns (MonitorAlbumResponse) {}
|
||||||
|
rpc GetArtists(GetArtistsRequest) returns (GetArtistsResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message MonitorAlbumRequest {
|
message MonitorAlbumRequest {
|
||||||
@@ -26,6 +27,30 @@ message MonitorAlbumResponse {
|
|||||||
MonitoredRelease release = 1;
|
MonitoredRelease release = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetArtistsRequest {}
|
||||||
|
|
||||||
|
message GetArtistsResponse {
|
||||||
|
repeated ArtistSummary artists = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ArtistMonitorState {
|
||||||
|
ARTIST_MONITOR_STATE_UNSPECIFIED = 0;
|
||||||
|
ARTIST_MONITOR_STATE_MONITORED = 1;
|
||||||
|
ARTIST_MONITOR_STATE_UNMONITORED = 2;
|
||||||
|
ARTIST_MONITOR_STATE_EXCLUDED = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ArtistSummary {
|
||||||
|
string id = 1;
|
||||||
|
string external_id = 2;
|
||||||
|
string name = 3;
|
||||||
|
string artist_type = 4;
|
||||||
|
string country = 5;
|
||||||
|
repeated string genres = 6;
|
||||||
|
string image_url = 7;
|
||||||
|
ArtistMonitorState monitor_state = 8;
|
||||||
|
}
|
||||||
|
|
||||||
message MonitoredRelease {
|
message MonitoredRelease {
|
||||||
string info_hash = 1;
|
string info_hash = 1;
|
||||||
string artist = 2;
|
string artist = 2;
|
||||||
|
|||||||
Reference in New Issue
Block a user