Skip to content

Commit 828b5ff

Browse files
committed
option to use blurred cover art for Now Playing page background
1 parent f48b2ba commit 828b5ff

File tree

6 files changed

+81
-38
lines changed

6 files changed

+81
-38
lines changed

backend/config.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ type TracksPageConfig struct {
111111
}
112112

113113
type NowPlayingPageConfig struct {
114-
InitialView string
114+
InitialView string
115+
UseBackgroundImage bool
115116
}
116117

117118
type PlaybackConfig struct {
@@ -242,7 +243,8 @@ func DefaultConfig(appVersionTag string) *Config {
242243
InitialView: "List",
243244
},
244245
NowPlayingConfig: NowPlayingPageConfig{
245-
InitialView: "Play Queue",
246+
InitialView: "Play Queue",
247+
UseBackgroundImage: true,
246248
},
247249
TracksPage: TracksPageConfig{
248250
TracklistColumns: []string{"Album", "Time", "Plays"},

go.mod

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
module github.com/dweymouth/supersonic
22

3-
go 1.23.0
3+
go 1.24.0
44

55
require (
66
fyne.io/fyne/v2 v2.6.1
77
github.com/20after4/configdir v0.1.1
88
github.com/Microsoft/go-winio v0.6.2
9+
github.com/boxes-ltd/imaging v1.7.1
910
github.com/cenkalti/dominantcolor v1.0.3
1011
github.com/charlievieth/strcase v0.0.5
1112
github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1
@@ -27,7 +28,7 @@ require (
2728
golang.org/x/net v0.38.0
2829
golang.org/x/sys v0.31.0
2930
golang.org/x/term v0.30.0
30-
golang.org/x/text v0.23.0
31+
golang.org/x/text v0.30.0
3132
)
3233

3334
require (
@@ -64,7 +65,7 @@ require (
6465
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
6566
github.com/stretchr/testify v1.10.0 // indirect
6667
github.com/yuin/goldmark v1.7.8 // indirect
67-
golang.org/x/image v0.24.0 // indirect
68+
golang.org/x/image v0.32.0 // indirect
6869
gopkg.in/yaml.v3 v3.0.1 // indirect
6970
)
7071

go.sum

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0
88
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
99
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
1010
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
11+
github.com/boxes-ltd/imaging v1.7.1 h1:JlfPQkr9sowLuPY24OLNw1Wa0UjriPVo2BN0YyxdCf0=
12+
github.com/boxes-ltd/imaging v1.7.1/go.mod h1:m89K2cMB8PXek+mUi/9SAha2A86+ZgfAOKjGypjMuDk=
1113
github.com/cenkalti/dominantcolor v1.0.3 h1:Pt0vfRZ8enkZh1n22RvoboA53SMM/v2aEwNQTZKSqww=
1214
github.com/cenkalti/dominantcolor v1.0.3/go.mod h1:mGpFMbWUnyXaGN48Zbf9bU9HJP1eCCD7dnsscb4lyR4=
1315
github.com/charlievieth/strcase v0.0.5 h1:gV4iXVyD6eI5KdfOV+/vIVCKXZwtCWOmDMcu7Uy00Rs=
@@ -142,16 +144,16 @@ github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
142144
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
143145
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
144146
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
145-
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
146-
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
147+
golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
148+
golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
147149
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
148150
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
149151
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
150152
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
151153
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
152154
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
153-
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
154-
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
155+
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
156+
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
155157
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
156158
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
157159
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

res/translations/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,9 @@
254254
"Unable to play artist radio": "Unable to play artist radio",
255255
"Unable to play random tracks": "Unable to play random tracks",
256256
"URL": "URL",
257+
"Use blurred album cover for Now Playing page background": "Use blurred album cover for Now Playing page background",
257258
"Use legacy authentication": "Use legacy authentication",
259+
"Use waveform seekbar": "Use waveform seekbar",
258260
"Username": "Username",
259261
"version": "version",
260262
"Visualizations": "Visualizations",

ui/browsing/nowplayingpage.go

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"slices"
1111
"strings"
1212

13+
"github.com/boxes-ltd/imaging"
1314
"github.com/cenkalti/dominantcolor"
1415
"github.com/dweymouth/supersonic/backend"
1516
"github.com/dweymouth/supersonic/backend/mediaprovider"
@@ -49,16 +50,18 @@ type NowPlayingPage struct {
4950
alreadyLoaded bool
5051

5152
// widgets for render
52-
background *canvas.LinearGradient
53-
queueList *widgets.PlayQueueList
54-
relatedList *widgets.PlayQueueList
55-
lyricsViewer *widgets.LyricsViewer
56-
card *widgets.LargeNowPlayingCard
57-
statusLabel *widget.Label
58-
tabs *container.AppTabs
59-
lyricsLoading *widgets.LoadingDots
60-
relatedLoading *widgets.LoadingDots
61-
container *fyne.Container
53+
backgroundImgA *canvas.Image
54+
backgroundImgB *canvas.Image
55+
backgroundGradient *canvas.LinearGradient
56+
queueList *widgets.PlayQueueList
57+
relatedList *widgets.PlayQueueList
58+
lyricsViewer *widgets.LyricsViewer
59+
card *widgets.LargeNowPlayingCard
60+
statusLabel *widget.Label
61+
tabs *container.AppTabs
62+
lyricsLoading *widgets.LoadingDots
63+
relatedLoading *widgets.LoadingDots
64+
container *fyne.Container
6265

6366
// cancel funcs for background fetch tasks
6467
imageLoadCancel context.CancelFunc
@@ -232,15 +235,19 @@ func (a *NowPlayingPage) CreateRenderer() fyne.WidgetRenderer {
232235
a.updateRelatedList()
233236
}
234237
c := theme.Color(myTheme.ColorNamePageBackground)
235-
a.background = canvas.NewLinearGradient(c, c, 0)
238+
a.backgroundGradient = canvas.NewLinearGradient(c, c, 0)
239+
a.backgroundImgA = canvas.NewImageFromImage(nil)
240+
a.backgroundImgB = canvas.NewImageFromImage(nil)
236241

237242
mainContent := container.NewGridWithColumns(2,
238243
container.New(paddedLayout, a.card),
239244
container.New(paddedLayout,
240245
util.AddHeaderBackgroundWithColorName(
241246
a.tabs, myTheme.ColorNameNowPlayingPanel)))
242247
a.container = container.NewStack(
243-
a.background,
248+
a.backgroundImgA,
249+
a.backgroundImgB,
250+
a.backgroundGradient,
244251
mainContent,
245252
container.NewVBox(
246253
layout.NewSpacer(),
@@ -318,20 +325,46 @@ func (a *NowPlayingPage) onImageLoaded(img image.Image, err error) {
318325
return
319326
}
320327

321-
c := dominantcolor.Find(img)
322-
if c == a.background.StartColor {
323-
return
324-
}
325-
326-
// Fyne animation starting is currently thread-safe,
327-
// despite not being marked as such
328-
// TODO: if this changes, use fyne.Do
329-
anim := canvas.NewColorRGBAAnimation(
330-
a.background.StartColor, c, myTheme.AnimationDurationMedium, func(c color.Color) {
331-
a.background.StartColor = c
332-
a.background.Refresh()
328+
if a.conf.UseBackgroundImage {
329+
resized := imaging.Resize(img, 300, 0, imaging.NearestNeighbor)
330+
blurred := imaging.Blur(resized, 10.0)
331+
fyne.Do(func() {
332+
if a.backgroundGradient.StartColor != color.Transparent {
333+
a.backgroundGradient.StartColor = color.Transparent
334+
a.backgroundGradient.Refresh()
335+
}
336+
a.backgroundImgA.Hidden = false
337+
a.backgroundImgB.Hidden = false
338+
a.backgroundImgA.Image = a.backgroundImgB.Image
339+
a.backgroundImgB.Image = blurred
340+
fyne.NewAnimation(myTheme.AnimationDurationMedium, func(f float32) {
341+
a.backgroundImgA.Translucency = float64(f)
342+
a.backgroundImgB.Translucency = float64(1 - f)
343+
a.backgroundImgA.Refresh()
344+
a.backgroundImgB.Refresh()
345+
}).Start()
333346
})
334-
anim.Start()
347+
} else {
348+
c := dominantcolor.Find(img)
349+
if c == a.backgroundGradient.StartColor {
350+
return
351+
}
352+
if !a.backgroundImgA.Hidden {
353+
a.backgroundImgA.Hide()
354+
}
355+
if !a.backgroundImgB.Hidden {
356+
a.backgroundImgB.Hide()
357+
}
358+
// Fyne animation starting is currently thread-safe,
359+
// despite not being marked as such
360+
// TODO: if this changes, use fyne.Do
361+
anim := canvas.NewColorRGBAAnimation(
362+
a.backgroundGradient.StartColor, c, myTheme.AnimationDurationMedium, func(c color.Color) {
363+
a.backgroundGradient.StartColor = c
364+
a.backgroundGradient.Refresh()
365+
})
366+
anim.Start()
367+
}
335368
}
336369

337370
func (a *NowPlayingPage) updateLyrics() {
@@ -486,11 +519,11 @@ func (a *NowPlayingPage) UnselectAll() {
486519
}
487520

488521
func (a *NowPlayingPage) Refresh() {
489-
if a.background != nil {
522+
if a.backgroundGradient != nil {
490523
c := theme.Color(myTheme.ColorNamePageBackground)
491-
if c != a.background.EndColor {
492-
a.background.EndColor = c
493-
a.background.Refresh()
524+
if c != a.backgroundGradient.EndColor {
525+
a.backgroundGradient.EndColor = c
526+
a.backgroundGradient.Refresh()
494527
}
495528
}
496529
a.BaseWidget.Refresh()

ui/dialogs/settingsdialog.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,8 @@ func (s *SettingsDialog) createAppearanceTab(window fyne.Window) *container.TabI
571571
})
572572
useWaveformSeekbar.Checked = s.config.Playback.UseWaveformSeekbar
573573

574+
nowPlayingBackground := widget.NewCheckWithData(lang.L("Use blurred album cover for Now Playing page background"), binding.BindBool(&s.config.NowPlayingConfig.UseBackgroundImage))
575+
574576
return container.NewTabItem(lang.L("Appearance"), container.NewVBox(
575577
util.NewHSpace(0), // insert a theme.Padding amount of space at top
576578
container.NewBorder(nil, nil, widget.NewLabel(lang.L("Theme")), /*left*/
@@ -583,6 +585,7 @@ func (s *SettingsDialog) createAppearanceTab(window fyne.Window) *container.TabI
583585
disableDPI,
584586
s.newSectionSeparator(),
585587
useWaveformSeekbar,
588+
nowPlayingBackground,
586589
s.newSectionSeparator(),
587590
widget.NewRichText(&widget.TextSegment{Text: lang.L("Application font"), Style: util.BoldRichTextStyle}),
588591
container.New(layout.NewFormLayout(),

0 commit comments

Comments
 (0)