Koito/internal/images/imagesrc.go
Gabe Farrell 36f984a1a2
Pre-release version v0.0.14 (#96)
* add dev branch container to workflow

* correctly set the default range of ActivityGrid

* fix: set name/short_name to koito (#61)

* fix dev container push workflow

* fix: race condition with using getComputedStyle primary color for dynamic activity grid darkening (#76)

* Fix race condition with using getComputedStyle primary color for dynamic activity grid darkening

Instead just use the color from the current theme directly. Tested works on initial load and theme changes.
Fixes https://github.com/gabehf/Koito/issues/75

* Rework theme provider to provide the actual Theme object throughtout the app, in addition to the name
Split name out of the Theme struct to simplify custom theme saving/reading

* fix: set first artist listed as primary by default (#81)

* feat: add server-side configuration with default theme (#90)

* docs: add example for usage of the main listenbrainz instance (#71)

* docs: add example for usage of the main listenbrainz instance

* Update scrobbler.md

---------

Co-authored-by: Gabe Farrell <90876006+gabehf@users.noreply.github.com>

* feat: add server-side cfg and default theme

* fix: repair custom theme

---------

Co-authored-by: m0d3rnX <jesper@posteo.de>

* docs: add default theme cfg option to docs

* feat: add ability to manually scrobble track (#91)

* feat: add button to manually scrobble from ui

* fix: ensure timestamp is in the past, log fix

* test: add integration test

* feat: add first listened to dates for media items (#92)

* fix: ensure error checks for ErrNoRows

* feat: add now playing endpoint and ui (#93)

* wip

* feat: add now playing

* fix: set default theme when config is not set

* feat: fetch images from subsonic server (#94)

* fix: useQuery instead of useEffect for now playing

* feat: custom artist separator regex (#95)

* Fix race condition with using getComputedStyle primary color for dynamic activity grid darkening

Instead just use the color from the current theme directly. Tested works on initial load and theme changes.
Fixes https://github.com/gabehf/Koito/issues/75

* Rework theme provider to provide the actual Theme object throughtout the app, in addition to the name
Split name out of the Theme struct to simplify custom theme saving/reading

* feat: add server-side configuration with default theme (#90)

* docs: add example for usage of the main listenbrainz instance (#71)

* docs: add example for usage of the main listenbrainz instance

* Update scrobbler.md

---------

Co-authored-by: Gabe Farrell <90876006+gabehf@users.noreply.github.com>

* feat: add server-side cfg and default theme

* fix: repair custom theme

---------

Co-authored-by: m0d3rnX <jesper@posteo.de>

* fix: rebase errors

---------

Co-authored-by: pet <128837728+againstpetra@users.noreply.github.com>
Co-authored-by: mlandry <mike.landry@gmail.com>
Co-authored-by: m0d3rnX <jesper@posteo.de>
2025-11-19 20:26:56 -05:00

134 lines
3.4 KiB
Go

// package imagesrc defines interfaces for album and artist image providers
package images
import (
"context"
"fmt"
"net/http"
"sync"
"github.com/gabehf/koito/internal/logger"
"github.com/google/uuid"
)
type ImageSource struct {
deezerEnabled bool
deezerC *DeezerClient
subsonicEnabled bool
subsonicC *SubsonicClient
caaEnabled bool
}
type ImageSourceOpts struct {
UserAgent string
EnableCAA bool
EnableDeezer bool
EnableSubsonic bool
}
var once sync.Once
var imgsrc ImageSource
type ArtistImageOpts struct {
Aliases []string
}
type AlbumImageOpts struct {
Artists []string
Album string
ReleaseMbzID *uuid.UUID
ReleaseGroupMbzID *uuid.UUID
}
const caaBaseUrl = "https://coverartarchive.org"
// all functions are no-op if no providers are enabled
func Initialize(opts ImageSourceOpts) {
once.Do(func() {
if opts.EnableCAA {
imgsrc.caaEnabled = true
}
if opts.EnableDeezer {
imgsrc.deezerEnabled = true
imgsrc.deezerC = NewDeezerClient()
}
if opts.EnableSubsonic {
imgsrc.subsonicEnabled = true
imgsrc.subsonicC = NewSubsonicClient()
}
})
}
func Shutdown() {
imgsrc.deezerC.Shutdown()
}
func GetArtistImage(ctx context.Context, opts ArtistImageOpts) (string, error) {
l := logger.FromContext(ctx)
if imgsrc.subsonicEnabled {
img, err := imgsrc.subsonicC.GetArtistImage(ctx, opts.Aliases[0])
if err != nil {
return "", err
}
if img != "" {
return img, nil
}
l.Debug().Msg("Could not find artist image from Subsonic")
}
if imgsrc.deezerC != nil {
img, err := imgsrc.deezerC.GetArtistImages(ctx, opts.Aliases)
if err != nil {
return "", err
}
return img, nil
}
l.Warn().Msg("GetArtistImage: No image providers are enabled")
return "", nil
}
func GetAlbumImage(ctx context.Context, opts AlbumImageOpts) (string, error) {
l := logger.FromContext(ctx)
if imgsrc.subsonicEnabled {
img, err := imgsrc.subsonicC.GetAlbumImage(ctx, opts.Artists[0], opts.Album)
if err != nil {
return "", err
}
if img != "" {
return img, nil
}
l.Debug().Msg("Could not find album cover from Subsonic")
}
if imgsrc.caaEnabled {
l.Debug().Msg("Attempting to find album image from CoverArtArchive")
if opts.ReleaseMbzID != nil && *opts.ReleaseMbzID != uuid.Nil {
url := fmt.Sprintf(caaBaseUrl+"/release/%s/front", opts.ReleaseMbzID.String())
resp, err := http.DefaultClient.Head(url)
if err != nil {
return "", err
}
if resp.StatusCode == 200 {
return url, nil
}
l.Debug().Str("url", url).Str("status", resp.Status).Msg("Could not find album cover from CoverArtArchive with MusicBrainz release ID")
}
if opts.ReleaseGroupMbzID != nil && *opts.ReleaseGroupMbzID != uuid.Nil {
url := fmt.Sprintf(caaBaseUrl+"/release-group/%s/front", opts.ReleaseGroupMbzID.String())
resp, err := http.DefaultClient.Head(url)
if err != nil {
return "", err
}
if resp.StatusCode == 200 {
return url, nil
}
l.Debug().Str("url", url).Str("status", resp.Status).Msg("Could not find album cover from CoverArtArchive with MusicBrainz release group ID")
}
}
if imgsrc.deezerEnabled {
l.Debug().Msg("Attempting to find album image from Deezer")
img, err := imgsrc.deezerC.GetAlbumImages(ctx, opts.Artists, opts.Album)
if err != nil {
return "", err
}
return img, nil
}
l.Warn().Msg("GetAlbumImage: No image providers are enabled")
return "", nil
}