Import missing artist portraits from uploaded album folders
ci / module boundaries / boundaries (push) Successful in 6s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 2m1s
ci / ts / ts (packages + web) (push) Failing after 5m13s
ci / api / api (push) Successful in 41s
ci / db / db (push) Failing after 1m33s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 2m25s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 2m19s
ci / module boundaries / boundaries (push) Successful in 6s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 2m1s
ci / ts / ts (packages + web) (push) Failing after 5m13s
ci / api / api (push) Successful in 41s
ci / db / db (push) Failing after 1m33s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 2m25s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 2m19s
This commit is contained in:
1 parent
fd17c06397
commit
82bf015283
23 files changed
+829
-317
No files matched your search
@@ -0,0 +1,74 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type artistImageStore interface {
|
||||
ArtistWithoutImageForFile(context.Context, uuid.UUID) (uuid.UUID, error)
|
||||
SetArtistImageIfMissing(context.Context, uuid.UUID, string) error
|
||||
}
|
||||
|
||||
type imageRenderer interface {
|
||||
StoreImage(context.Context, []byte) (string, error)
|
||||
}
|
||||
|
||||
// ArtistImages applies approved portraits to the album artist, never guests.
|
||||
type ArtistImages struct {
|
||||
store artistImageStore
|
||||
images imageRenderer
|
||||
}
|
||||
|
||||
func NewArtistImages(store *Store, covers *Covers) *ArtistImages {
|
||||
return &ArtistImages{store: store, images: covers}
|
||||
}
|
||||
|
||||
func (a *ArtistImages) ApplyArtistImage(ctx context.Context, file uuid.UUID, data []byte) error {
|
||||
id, err := a.store.ArtistWithoutImageForFile(ctx, file)
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := a.images.StoreImage(ctx, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.store.SetArtistImageIfMissing(ctx, id, key)
|
||||
}
|
||||
|
||||
func (c *Covers) StoreImage(ctx context.Context, data []byte) (string, error) {
|
||||
if err := os.MkdirAll(c.Dir, 0o755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
sum := sha256.Sum256(data)
|
||||
key := hex.EncodeToString(sum[:8])
|
||||
_, err := c.render(ctx, data, key)
|
||||
return key, err
|
||||
}
|
||||
|
||||
func (st *Store) ArtistWithoutImageForFile(ctx context.Context, file uuid.UUID) (uuid.UUID, error) {
|
||||
var id uuid.UUID
|
||||
err := st.pool.QueryRow(ctx, `SELECT a.id FROM media_files f JOIN tracks t ON t.id=f.track_id
|
||||
JOIN albums al ON al.id=t.album_id JOIN artists a ON a.id=al.album_artist_id
|
||||
WHERE f.id=$1 AND a.image_path IS NULL AND a.genius_image_url IS NULL`, file).Scan(&id)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return id, ErrNotFound
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
|
||||
func (st *Store) SetArtistImageIfMissing(ctx context.Context, id uuid.UUID, key string) error {
|
||||
// Another import or Genius may have supplied a portrait during rendering.
|
||||
_, err := st.pool.Exec(ctx, `UPDATE artists SET image_path=$2 WHERE id=$1
|
||||
AND image_path IS NULL AND genius_image_url IS NULL`, id, key)
|
||||
return err
|
||||
}
|
||||
Reference in new issue
Block a user