diff --git a/conf/configuration.go b/conf/configuration.go index abb891138..de29216cc 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -88,6 +88,7 @@ type configOptions struct { DefaultLanguage string DefaultUIVolume int UISearchDebounceMs int + UICoverArtSize int EnableReplayGain bool EnableCoverAnimation bool EnableNowPlaying bool @@ -729,6 +730,7 @@ func setViperDefaults() { viper.SetDefault("defaultlanguage", "") viper.SetDefault("defaultuivolume", consts.DefaultUIVolume) viper.SetDefault("uisearchdebouncems", consts.DefaultUISearchDebounceMs) + viper.SetDefault("uicoverartsize", consts.DefaultUICoverArtSize) viper.SetDefault("enablereplaygain", true) viper.SetDefault("enablecoveranimation", true) viper.SetDefault("enablenowplaying", true) diff --git a/consts/consts.go b/consts/consts.go index f1010a872..e4888b10b 100644 --- a/consts/consts.go +++ b/consts/consts.go @@ -85,11 +85,9 @@ const ( ) const ( - UICoverArtSize = 600 + DefaultUICoverArtSize = 600 ) -var CacheWarmerImageSizes = []int{UICoverArtSize} - // Prometheus options const ( PrometheusDefaultPath = "/metrics" diff --git a/core/artwork/cache_warmer.go b/core/artwork/cache_warmer.go index bd1359b74..3dddd2c6f 100644 --- a/core/artwork/cache_warmer.go +++ b/core/artwork/cache_warmer.go @@ -10,7 +10,6 @@ import ( "time" "github.com/navidrome/navidrome/conf" - "github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/log" "github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/model/request" @@ -24,7 +23,7 @@ type CacheWarmer interface { // NewCacheWarmer creates a new CacheWarmer instance. The CacheWarmer will pre-cache Artwork images in the background // to speed up the response time when the image is requested by the UI. The cache is pre-populated with the original -// image size, as well as the size defined in the UICoverArtSize constant. +// image size, as well as the size defined by the UICoverArtSize config option. func NewCacheWarmer(artwork Artwork, cache cache.FileCache) CacheWarmer { // If image cache is disabled, return a NOOP implementation if conf.Server.ImageCacheSize == "0" || !conf.Server.EnableArtworkPrecache { @@ -142,16 +141,14 @@ func (a *cacheWarmer) doCacheImage(ctx context.Context, id model.ArtworkID) erro ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() - for _, size := range consts.CacheWarmerImageSizes { - r, _, err := a.artwork.Get(ctx, id, size, true) - if err != nil { - return fmt.Errorf("caching id='%s', size=%d: %w", id, size, err) - } - _, err = io.Copy(io.Discard, r) - r.Close() - return err + size := conf.Server.UICoverArtSize + r, _, err := a.artwork.Get(ctx, id, size, true) + if err != nil { + return fmt.Errorf("caching id='%s', size=%d: %w", id, size, err) } - return nil + _, err = io.Copy(io.Discard, r) + r.Close() + return err } func NoopCacheWarmer() CacheWarmer { diff --git a/core/artwork/cache_warmer_test.go b/core/artwork/cache_warmer_test.go index 9798ea8d6..a5da2004c 100644 --- a/core/artwork/cache_warmer_test.go +++ b/core/artwork/cache_warmer_test.go @@ -12,7 +12,6 @@ import ( "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/conf/configtest" - "github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/utils/cache" . "github.com/onsi/ginkgo/v2" @@ -182,7 +181,7 @@ var _ = Describe("CacheWarmer", func() { Eventually(func() []int { return aw.getCachedSizes() - }).Should(ContainElements(consts.UICoverArtSize)) + }).Should(ContainElements(conf.Server.UICoverArtSize)) }) }) }) diff --git a/core/metrics/insights.go b/core/metrics/insights.go index 1dcd13b25..f069d3fb6 100644 --- a/core/metrics/insights.go +++ b/core/metrics/insights.go @@ -196,6 +196,7 @@ var staticData = sync.OnceValue(func() insights.Data { data.Config.EnableArtworkUpload = conf.Server.EnableArtworkUpload data.Config.CoverArtQuality = conf.Server.CoverArtQuality data.Config.EnableWebPEncoding = conf.Server.EnableWebPEncoding + data.Config.UICoverArtSize = conf.Server.UICoverArtSize data.Config.EnableCoverAnimation = conf.Server.EnableCoverAnimation data.Config.EnableNowPlaying = conf.Server.EnableNowPlaying data.Config.EnableDownloads = conf.Server.EnableDownloads diff --git a/core/metrics/insights/data.go b/core/metrics/insights/data.go index 06a20b6f8..34648a49b 100644 --- a/core/metrics/insights/data.go +++ b/core/metrics/insights/data.go @@ -66,6 +66,7 @@ type Data struct { EnableArtworkUpload bool `json:"enableArtworkUpload,omitempty"` CoverArtQuality int `json:"coverArtQuality,omitempty"` EnableWebPEncoding bool `json:"enableWebPEncoding,omitempty"` + UICoverArtSize int `json:"uiCoverArtSize,omitempty"` EnableCoverAnimation bool `json:"enableCoverAnimation,omitempty"` EnableNowPlaying bool `json:"enableNowPlaying,omitempty"` SessionTimeout uint64 `json:"sessionTimeout,omitempty"` diff --git a/server/public/handle_shares.go b/server/public/handle_shares.go index 15e63d4db..24ecff1d6 100644 --- a/server/public/handle_shares.go +++ b/server/public/handle_shares.go @@ -6,6 +6,7 @@ import ( "net/http" "path" + "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/core/auth" "github.com/navidrome/navidrome/core/publicurl" @@ -81,7 +82,7 @@ func checkShareError(ctx context.Context, w http.ResponseWriter, err error, id s func (pub *Router) mapShareInfo(r *http.Request, s model.Share) *model.Share { s.URL = ShareURL(r, s.ID) - s.ImageURL = publicurl.ImageURL(r, s.CoverArtID(), consts.UICoverArtSize) + s.ImageURL = publicurl.ImageURL(r, s.CoverArtID(), conf.Server.UICoverArtSize) for i := range s.Tracks { s.Tracks[i].ID = encodeMediafileShare(s, s.Tracks[i].ID) } diff --git a/server/serve_index.go b/server/serve_index.go index 0d1a2f330..bd5be44f5 100644 --- a/server/serve_index.go +++ b/server/serve_index.go @@ -55,6 +55,7 @@ func serveIndex(ds model.DataStore, fs fs.FS, shareInfo *model.Share) http.Handl "defaultLanguage": conf.Server.DefaultLanguage, "defaultUIVolume": conf.Server.DefaultUIVolume, "uiSearchDebounceMs": conf.Server.UISearchDebounceMs, + "uiCoverArtSize": conf.Server.UICoverArtSize, "enableCoverAnimation": conf.Server.EnableCoverAnimation, "enableNowPlaying": conf.Server.EnableNowPlaying, "gaTrackingId": conf.Server.GATrackingID, diff --git a/server/serve_index_test.go b/server/serve_index_test.go index e08a42643..7515e7276 100644 --- a/server/serve_index_test.go +++ b/server/serve_index_test.go @@ -86,6 +86,7 @@ var _ = Describe("serveIndex", func() { Entry("defaultLanguage", func() { conf.Server.DefaultLanguage = "pt" }, "defaultLanguage", "pt"), Entry("defaultUIVolume", func() { conf.Server.DefaultUIVolume = 45 }, "defaultUIVolume", float64(45)), Entry("uiSearchDebounceMs", func() { conf.Server.UISearchDebounceMs = 500 }, "uiSearchDebounceMs", float64(500)), + Entry("uiCoverArtSize", func() { conf.Server.UICoverArtSize = 300 }, "uiCoverArtSize", float64(300)), Entry("enableCoverAnimation", func() { conf.Server.EnableCoverAnimation = true }, "enableCoverAnimation", true), Entry("enableNowPlaying", func() { conf.Server.EnableNowPlaying = true }, "enableNowPlaying", true), Entry("gaTrackingId", func() { conf.Server.GATrackingID = "UA-12345" }, "gaTrackingId", "UA-12345"), diff --git a/ui/src/album/AlbumDetails.jsx b/ui/src/album/AlbumDetails.jsx index 2411b8611..cec66eb8b 100644 --- a/ui/src/album/AlbumDetails.jsx +++ b/ui/src/album/AlbumDetails.jsx @@ -18,7 +18,7 @@ import { useTranslate, } from 'react-admin' import Lightbox from 'react-image-lightbox' -import { COVER_ART_SIZE } from '../consts' +import config from '../config' import 'react-image-lightbox/style.css' import subsonic from '../subsonic' import { @@ -32,7 +32,6 @@ import { useAlbumsPerPage, useImageLoadingState, } from '../common' -import config from '../config' import { formatFullDate, intersperse } from '../utils' import AlbumExternalLinks from './AlbumExternalLinks' import { SafeHTML } from '../common/SafeHTML' @@ -255,7 +254,7 @@ const AlbumDetails = (props) => { }) }, [record]) - const imageUrl = subsonic.getCoverArtUrl(record, COVER_ART_SIZE) + const imageUrl = subsonic.getCoverArtUrl(record, config.uiCoverArtSize) const fullImageUrl = subsonic.getCoverArtUrl(record) return ( diff --git a/ui/src/album/AlbumGridView.jsx b/ui/src/album/AlbumGridView.jsx index c8a161571..9717618fa 100644 --- a/ui/src/album/AlbumGridView.jsx +++ b/ui/src/album/AlbumGridView.jsx @@ -20,7 +20,8 @@ import { OverflowTooltip, useImageUrl, } from '../common' -import { COVER_ART_SIZE, DraggableTypes } from '../consts' +import config from '../config' +import { DraggableTypes } from '../consts' import clsx from 'clsx' import { AlbumDatesField } from './AlbumDatesField.jsx' @@ -135,7 +136,7 @@ const Cover = withContentRect('bounds')(({ [record], ) - const url = subsonic.getCoverArtUrl(record, COVER_ART_SIZE, true) + const url = subsonic.getCoverArtUrl(record, config.uiCoverArtSize, true) const { imgUrl, loading: imageLoading } = useImageUrl(url) return ( diff --git a/ui/src/artist/DesktopArtistDetails.jsx b/ui/src/artist/DesktopArtistDetails.jsx index bc2312477..dda761097 100644 --- a/ui/src/artist/DesktopArtistDetails.jsx +++ b/ui/src/artist/DesktopArtistDetails.jsx @@ -15,7 +15,6 @@ import { import Lightbox from 'react-image-lightbox' import ExpandInfoDialog from '../dialogs/ExpandInfoDialog' import AlbumInfo from '../album/AlbumInfo' -import { COVER_ART_SIZE } from '../consts' import subsonic from '../subsonic' import { SafeHTML } from '../common/SafeHTML' @@ -110,7 +109,7 @@ const DesktopArtistDetails = ({ artistInfo, record, biography }) => { { { handleCloseLightbox, } = useImageLoadingState(record.id) - const imageUrl = subsonic.getCoverArtUrl(record, COVER_ART_SIZE, true) + const imageUrl = subsonic.getCoverArtUrl(record, config.uiCoverArtSize, true) const fullImageUrl = subsonic.getCoverArtUrl(record) return ( diff --git a/ui/src/radio/RadioEdit.jsx b/ui/src/radio/RadioEdit.jsx index 5f804535a..bbe001e6f 100644 --- a/ui/src/radio/RadioEdit.jsx +++ b/ui/src/radio/RadioEdit.jsx @@ -11,7 +11,8 @@ import { makeStyles } from '@material-ui/core/styles' import { urlValidate } from '../utils/validations' import { Title, ImageUploadOverlay, useImageLoadingState } from '../common' import subsonic from '../subsonic' -import { COVER_ART_SIZE, RADIO_PLACEHOLDER_IMAGE } from '../consts' +import config from '../config' +import { RADIO_PLACEHOLDER_IMAGE } from '../consts' const useStyles = makeStyles({ coverParent: { @@ -83,7 +84,7 @@ const RadioCoverArt = ({ record }) => { {record.uploadedImage ? ( { @@ -31,7 +31,7 @@ describe('getCoverArtUrl', () => { updatedAt: '2023-01-01T00:00:00Z', } - const url = subsonic.getCoverArtUrl(playlistRecord, COVER_ART_SIZE, true) + const url = subsonic.getCoverArtUrl(playlistRecord, config.uiCoverArtSize, true) expect(url).toContain('pl-playlist-123') expect(url).toContain('size=600') @@ -45,7 +45,7 @@ describe('getCoverArtUrl', () => { sync: true, } - const url = subsonic.getCoverArtUrl(playlistRecord, COVER_ART_SIZE, true) + const url = subsonic.getCoverArtUrl(playlistRecord, config.uiCoverArtSize, true) expect(url).toContain('pl-playlist-123') expect(url).toContain('size=600') @@ -60,7 +60,7 @@ describe('getCoverArtUrl', () => { updatedAt: '2023-01-01T00:00:00Z', } - const url = subsonic.getCoverArtUrl(albumRecord, COVER_ART_SIZE, true) + const url = subsonic.getCoverArtUrl(albumRecord, config.uiCoverArtSize, true) expect(url).toContain('al-album-123') expect(url).toContain('size=600') @@ -74,7 +74,7 @@ describe('getCoverArtUrl', () => { updatedAt: '2023-01-01T00:00:00Z', } - const url = subsonic.getCoverArtUrl(songRecord, COVER_ART_SIZE, true) + const url = subsonic.getCoverArtUrl(songRecord, config.uiCoverArtSize, true) expect(url).toContain('mf-song-123') expect(url).toContain('size=600') @@ -87,7 +87,7 @@ describe('getCoverArtUrl', () => { updatedAt: '2023-01-01T00:00:00Z', } - const url = subsonic.getCoverArtUrl(artistRecord, COVER_ART_SIZE, true) + const url = subsonic.getCoverArtUrl(artistRecord, config.uiCoverArtSize, true) expect(url).toContain('ar-artist-123') expect(url).toContain('size=600')