feat(genius): load song metadata, lyrics, and artwork
ci / ts / ts (packages + web) (push) Canceled after 1m0s
ci / api / api (push) Successful in 46s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 56s
ci / db / db (push) Successful in 1m32s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m27s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m31s
ci / ts / ts (packages + web) (push) Canceled after 1m0s
ci / api / api (push) Successful in 46s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 56s
ci / db / db (push) Successful in 1m32s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m27s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m31s
Search hits omit album covers and artist images. The worker now reads the full song and artist, stores the description, and leaves Genius image URLs unchanged on mobile. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
fb20258489
commit
73ad61e7a5
34 files changed
+2214
-303
No files matched your search
@@ -18,13 +18,14 @@ type Ref struct {
|
||||
|
||||
// AlbumRow — альбом для карточек и страниц.
|
||||
type AlbumRow struct {
|
||||
ID uuid.UUID
|
||||
Title string
|
||||
Year *int
|
||||
CoverPath *string
|
||||
CoverColors []string
|
||||
Artist Ref
|
||||
TrackCount int
|
||||
ID uuid.UUID
|
||||
Title string
|
||||
Year *int
|
||||
CoverPath *string
|
||||
ExternalCoverURL *string
|
||||
CoverColors []string
|
||||
Artist Ref
|
||||
TrackCount int
|
||||
}
|
||||
|
||||
// TrackRow — трек с исполнителями, альбомом и жанрами.
|
||||
@@ -47,6 +48,7 @@ type TrackRow struct {
|
||||
|
||||
// ArtistRow — исполнитель со счётчиками.
|
||||
type ArtistRow struct {
|
||||
ImageURL *string
|
||||
ID uuid.UUID
|
||||
Name string
|
||||
AlbumCount int
|
||||
@@ -59,7 +61,7 @@ const trackSelect = `
|
||||
SELECT t.id, t.title, t.duration_ms, t.disc_no, t.track_no,
|
||||
COALESCE((SELECT json_agg(json_build_object('id', a.id, 'name', a.name) ORDER BY ta.role DESC, ta.position)
|
||||
FROM track_artists ta JOIN artists a ON a.id = ta.artist_id WHERE ta.track_id = t.id), '[]'),
|
||||
al.id, al.title, al.year, al.cover_path, COALESCE(al.cover_colors, '{}'), aa.id, aa.name,
|
||||
al.id, al.title, al.year, al.cover_path, COALESCE(al.genius_cover_url, (SELECT NULLIF(content->>'song_image_url','') FROM genius_tracks WHERE track_id=t.id)), COALESCE(al.cover_colors, '{}'), aa.id, aa.name,
|
||||
COALESCE((SELECT array_agg(g.name ORDER BY g.name) FROM track_genres tg JOIN genres g ON g.id = tg.genre_id
|
||||
WHERE tg.track_id = t.id), '{}'),
|
||||
EXISTS (SELECT 1 FROM media_files f JOIN library_sources s ON s.id = f.source_id
|
||||
@@ -76,7 +78,7 @@ func scanTrack(row pgx.Row) (TrackRow, error) {
|
||||
artists []byte
|
||||
)
|
||||
err := row.Scan(&t.ID, &t.Title, &t.DurationMs, &t.DiscNo, &t.TrackNo, &artists,
|
||||
&t.Album.ID, &t.Album.Title, &t.Album.Year, &t.Album.CoverPath, &t.Album.CoverColors,
|
||||
&t.Album.ID, &t.Album.Title, &t.Album.Year, &t.Album.CoverPath, &t.Album.ExternalCoverURL, &t.Album.CoverColors,
|
||||
&t.Album.Artist.ID, &t.Album.Artist.Name, &t.Genres, &t.Available, &t.Analyzed, &t.BPM, &t.Energy,
|
||||
&t.LoudnessLUFS)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
@@ -147,13 +149,13 @@ func (st *Store) AlbumTracks(ctx context.Context, albumID uuid.UUID) ([]TrackRow
|
||||
}
|
||||
|
||||
const albumSelect = `
|
||||
SELECT al.id, al.title, al.year, al.cover_path, COALESCE(al.cover_colors, '{}'), aa.id, aa.name,
|
||||
SELECT al.id, al.title, al.year, al.cover_path, al.genius_cover_url, COALESCE(al.cover_colors, '{}'), aa.id, aa.name,
|
||||
(SELECT count(*) FROM tracks t WHERE t.album_id = al.id)
|
||||
FROM albums al JOIN artists aa ON aa.id = al.album_artist_id`
|
||||
|
||||
func scanAlbum(row pgx.Row) (AlbumRow, error) {
|
||||
var a AlbumRow
|
||||
err := row.Scan(&a.ID, &a.Title, &a.Year, &a.CoverPath, &a.CoverColors, &a.Artist.ID, &a.Artist.Name, &a.TrackCount)
|
||||
err := row.Scan(&a.ID, &a.Title, &a.Year, &a.CoverPath, &a.ExternalCoverURL, &a.CoverColors, &a.Artist.ID, &a.Artist.Name, &a.TrackCount)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return AlbumRow{}, ErrNotFound
|
||||
}
|
||||
@@ -194,10 +196,10 @@ func (st *Store) ArtistAlbums(ctx context.Context, artistID uuid.UUID) ([]AlbumR
|
||||
func (st *Store) Artist(ctx context.Context, id uuid.UUID) (ArtistRow, error) {
|
||||
var a ArtistRow
|
||||
err := st.pool.QueryRow(ctx, `
|
||||
SELECT a.id, a.name,
|
||||
SELECT a.id, a.name, a.genius_image_url,
|
||||
(SELECT count(*) FROM albums al WHERE al.album_artist_id = a.id),
|
||||
(SELECT count(DISTINCT ta.track_id) FROM track_artists ta WHERE ta.artist_id = a.id)
|
||||
FROM artists a WHERE a.id = $1`, id).Scan(&a.ID, &a.Name, &a.AlbumCount, &a.TrackCount)
|
||||
FROM artists a WHERE a.id = $1`, id).Scan(&a.ID, &a.Name, &a.ImageURL, &a.AlbumCount, &a.TrackCount)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return ArtistRow{}, ErrNotFound
|
||||
}
|
||||
|
||||
@@ -393,6 +393,9 @@ func (d DTO) albumRef(a AlbumRow) gen.AlbumRef {
|
||||
|
||||
// coverURL — подписанная ссылка на обложку без размера (клиент добавляет &size=); nil — обложки нет.
|
||||
func (d DTO) coverURL(a AlbumRow) *string {
|
||||
if a.CoverPath == nil {
|
||||
return a.ExternalCoverURL
|
||||
}
|
||||
if a.CoverPath == nil || d.Signer == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -442,7 +445,7 @@ func (h *Handler) GetArtist(ctx context.Context, req gen.GetArtistRequestObject)
|
||||
if err != nil {
|
||||
return nil, catalogErr(err)
|
||||
}
|
||||
return gen.GetArtist200JSONResponse{Id: a.ID, Name: a.Name, AlbumCount: a.AlbumCount, TrackCount: a.TrackCount}, nil
|
||||
return gen.GetArtist200JSONResponse{Id: a.ID, Name: a.Name, ImageUrl: a.ImageURL, AlbumCount: a.AlbumCount, TrackCount: a.TrackCount}, nil
|
||||
}
|
||||
|
||||
// ListArtistAlbums реализует gen.StrictServerInterface.
|
||||
|
||||
Reference in new issue
Block a user