Fix #741: add album year to now playing

This commit is contained in:
zacaj
2025-11-23 19:14:40 +00:00
parent a884795a78
commit 9fdb62ab56
9 changed files with 59 additions and 31 deletions
+1
View File
@@ -210,6 +210,7 @@
"Show notification on track change": "Show notification on track change", "Show notification on track change": "Show notification on track change",
"Show play queue": "Show play queue", "Show play queue": "Show play queue",
"Show year in album grid cards": "Show year in album grid cards", "Show year in album grid cards": "Show year in album grid cards",
"Show year in with album name": "Show year in with album name",
"Shuffle": "Shuffle", "Shuffle": "Shuffle",
"Shuffled": "Shuffled", "Shuffled": "Shuffled",
"Shuffle albums": "Shuffle albums", "Shuffle albums": "Shuffle albums",
+5 -7
View File
@@ -28,7 +28,7 @@ type BottomPanel struct {
var _ fyne.Widget = (*BottomPanel)(nil) var _ fyne.Widget = (*BottomPanel)(nil)
func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr *controller.Controller, useWaveformSeekbar bool) *BottomPanel { func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr *controller.Controller, cfg *backend.Config) *BottomPanel {
bp := &BottomPanel{} bp := &BottomPanel{}
bp.ExtendBaseWidget(bp) bp.ExtendBaseWidget(bp)
@@ -46,7 +46,7 @@ func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr
pm.OnPlaying(util.FyneDoFunc(func() { bp.Controls.SetPlaying(true) })) pm.OnPlaying(util.FyneDoFunc(func() { bp.Controls.SetPlaying(true) }))
pm.OnStopped(util.FyneDoFunc(func() { bp.Controls.SetPlaying(false) })) pm.OnStopped(util.FyneDoFunc(func() { bp.Controls.SetPlaying(false) }))
bp.NowPlaying = widgets.NewNowPlayingCard() bp.NowPlaying = widgets.NewNowPlayingCard(cfg)
bp.NowPlaying.OnCoverTapped = func() { bp.NowPlaying.OnCoverTapped = func() {
contr.NavigateTo(controller.NowPlayingRoute()) contr.NavigateTo(controller.NowPlayingRoute())
} }
@@ -65,10 +65,8 @@ func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr
contr.DoAddTracksToPlaylistWorkflow([]string{tr.ID}) contr.DoAddTracksToPlaylistWorkflow([]string{tr.ID})
} }
} }
bp.NowPlaying.OnAlbumNameTapped = func() { bp.NowPlaying.OnAlbumNameTapped = func(albumID string) {
if tr, ok := pm.NowPlaying().(*mediaprovider.Track); ok { contr.NavigateTo(controller.AlbumRoute(albumID))
contr.NavigateTo(controller.AlbumRoute(tr.AlbumID))
}
} }
bp.NowPlaying.OnArtistNameTapped = func(artistID string) { bp.NowPlaying.OnArtistNameTapped = func(artistID string) {
contr.NavigateTo(controller.ArtistRoute(artistID)) contr.NavigateTo(controller.ArtistRoute(artistID))
@@ -86,7 +84,7 @@ func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr
contr.ShowShareDialog(tr.ID) contr.ShowShareDialog(tr.ID)
} }
} }
bp.Controls = widgets.NewPlayerControls(useWaveformSeekbar) bp.Controls = widgets.NewPlayerControls(cfg.Playback.UseWaveformSeekbar)
bp.Controls.OnPlayPause(func() { bp.Controls.OnPlayPause(func() {
pm.PlayPause() pm.PlayPause()
}) })
+7 -3
View File
@@ -79,6 +79,7 @@ type nowPlayingPageState struct {
mp mediaprovider.MediaProvider mp mediaprovider.MediaProvider
canRate bool canRate bool
canShare bool canShare bool
cfg *backend.Config
} }
func NewNowPlayingPage( func NewNowPlayingPage(
@@ -92,9 +93,10 @@ func NewNowPlayingPage(
mp mediaprovider.MediaProvider, mp mediaprovider.MediaProvider,
canRate bool, canRate bool,
canShare bool, canShare bool,
cfg *backend.Config,
) *NowPlayingPage { ) *NowPlayingPage {
state := nowPlayingPageState{ state := nowPlayingPageState{
conf: conf, contr: contr, pool: pool, sm: sm, lm: lm, im: im, pm: pm, mp: mp, canRate: canRate, canShare: canShare, conf: conf, contr: contr, pool: pool, sm: sm, lm: lm, im: im, pm: pm, mp: mp, canRate: canRate, canShare: canShare, cfg: cfg,
} }
if page, ok := pool.Obtain(util.WidgetTypeNowPlayingPage).(*NowPlayingPage); ok && page != nil { if page, ok := pool.Obtain(util.WidgetTypeNowPlayingPage).(*NowPlayingPage); ok && page != nil {
page.nowPlayingPageState = state page.nowPlayingPageState = state
@@ -109,7 +111,7 @@ func NewNowPlayingPage(
pm.OnPlaying(doFmtStatus) pm.OnPlaying(doFmtStatus)
pm.OnStopped(doFmtStatus) pm.OnStopped(doFmtStatus)
a.card = widgets.NewLargeNowPlayingCard() a.card = widgets.NewLargeNowPlayingCard(cfg)
a.card.OnAlbumNameTapped = func() { a.card.OnAlbumNameTapped = func() {
contr.NavigateTo(controller.AlbumRoute(a.nowPlaying.Metadata().AlbumID)) contr.NavigateTo(controller.AlbumRoute(a.nowPlaying.Metadata().AlbumID))
} }
@@ -474,7 +476,7 @@ func (a *NowPlayingPage) Reload() {
} }
func (s *nowPlayingPageState) Restore() Page { func (s *nowPlayingPageState) Restore() Page {
return NewNowPlayingPage(s.conf, s.contr, s.pool, s.sm, s.lm, s.im, s.pm, s.mp, s.canRate, s.canShare) return NewNowPlayingPage(s.conf, s.contr, s.pool, s.sm, s.lm, s.im, s.pm, s.mp, s.canRate, s.canShare, s.cfg)
} }
var _ CanShowPlayTime = (*NowPlayingPage)(nil) var _ CanShowPlayTime = (*NowPlayingPage)(nil)
@@ -527,6 +529,8 @@ func (a *NowPlayingPage) Refresh() {
} }
} }
a.BaseWidget.Refresh() a.BaseWidget.Refresh()
a.card.Update(a.nowPlaying)
} }
func (a *NowPlayingPage) saveSelectedTab(tabNum int) { func (a *NowPlayingPage) saveSelectedTab(tabNum int) {
+1 -1
View File
@@ -50,7 +50,7 @@ func (r Router) CreatePage(rte controller.Route) Page {
case controller.Genres: case controller.Genres:
return NewGenresPage(r.Controller, r.App.ServerManager.Server) return NewGenresPage(r.Controller, r.App.ServerManager.Server)
case controller.NowPlaying: case controller.NowPlaying:
return NewNowPlayingPage(&r.App.Config.NowPlayingConfig, r.Controller, r.widgetPool, r.App.ServerManager, r.App.LyricsManager, r.App.ImageManager, r.App.PlaybackManager, r.App.ServerManager.Server, canRate, canShare) return NewNowPlayingPage(&r.App.Config.NowPlayingConfig, r.Controller, r.widgetPool, r.App.ServerManager, r.App.LyricsManager, r.App.ImageManager, r.App.PlaybackManager, r.App.ServerManager.Server, canRate, canShare, r.App.Config)
case controller.Playlist: case controller.Playlist:
return NewPlaylistPage(rte.Arg, &r.App.Config.PlaylistPage, r.widgetPool, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager) return NewPlaylistPage(rte.Arg, &r.App.Config.PlaylistPage, r.widgetPool, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager)
case controller.Playlists: case controller.Playlists:
+1 -1
View File
@@ -184,7 +184,7 @@ func (s *SettingsDialog) createGeneralTab(canSaveQueueToServer bool) *container.
trackNotif := widget.NewCheckWithData(lang.L("Show notification on track change"), trackNotif := widget.NewCheckWithData(lang.L("Show notification on track change"),
binding.BindBool(&s.config.Application.ShowTrackChangeNotification)) binding.BindBool(&s.config.Application.ShowTrackChangeNotification))
albumGridYears := widget.NewCheck(lang.L("Show year in album grid cards"), func(b bool) { albumGridYears := widget.NewCheck(lang.L("Show year in with album name"), func(b bool) {
s.config.AlbumsPage.ShowYears = b s.config.AlbumsPage.ShowYears = b
s.config.FavoritesPage.ShowAlbumYears = b s.config.FavoritesPage.ShowAlbumYears = b
if s.OnPageNeedsRefresh != nil { if s.OnPageNeedsRefresh != nil {
+6 -2
View File
@@ -85,7 +85,11 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string,
m.Controller.NavHandler = m.Router.NavigateTo m.Controller.NavHandler = m.Router.NavigateTo
m.Controller.ReloadFunc = m.BrowsingPane.Reload m.Controller.ReloadFunc = m.BrowsingPane.Reload
m.Controller.CurPageFunc = m.BrowsingPane.CurrentPage m.Controller.CurPageFunc = m.BrowsingPane.CurrentPage
m.Controller.RefreshPageFunc = m.BrowsingPane.RefreshPage m.Controller.RefreshPageFunc = func() {
m.BrowsingPane.RefreshPage()
m.BottomPanel.onSongChange(app.PlaybackManager.NowPlaying(), nil)
}
m.Controller.SelectAllPageFunc = m.BrowsingPane.SelectAll m.Controller.SelectAllPageFunc = m.BrowsingPane.SelectAll
m.Controller.UnselectAllPageFunc = m.BrowsingPane.UnselectAll m.Controller.UnselectAllPageFunc = m.BrowsingPane.UnselectAll
m.Controller.ToastProvider = m.ToastOverlay m.Controller.ToastProvider = m.ToastOverlay
@@ -102,7 +106,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string,
)) ))
} }
m.BottomPanel = NewBottomPanel(app.PlaybackManager, app.ImageManager, m.Controller, m.Controller.App.Config.Playback.UseWaveformSeekbar) m.BottomPanel = NewBottomPanel(app.PlaybackManager, app.ImageManager, m.Controller, app.Config)
app.PlaybackManager.OnSongChange(func(item mediaprovider.MediaItem, _ *mediaprovider.Track) { app.PlaybackManager.OnSongChange(func(item mediaprovider.MediaItem, _ *mediaprovider.Track) {
fyne.Do(func() { m.UpdateOnTrackChange(item) }) fyne.Do(func() { m.UpdateOnTrackChange(item) })
}) })
+12 -1
View File
@@ -2,6 +2,7 @@ package widgets
import ( import (
"image" "image"
"strconv"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
@@ -9,6 +10,7 @@ import (
"fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"github.com/dweymouth/supersonic/backend"
"github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/mediaprovider"
myTheme "github.com/dweymouth/supersonic/ui/theme" myTheme "github.com/dweymouth/supersonic/ui/theme"
"github.com/dweymouth/supersonic/ui/util" "github.com/dweymouth/supersonic/ui/util"
@@ -29,6 +31,7 @@ type LargeNowPlayingCard struct {
favorite *FavoriteIcon favorite *FavoriteIcon
ratingFavoriteContainer *fyne.Container ratingFavoriteContainer *fyne.Container
cover *ImagePlaceholder cover *ImagePlaceholder
cfg *backend.Config
OnArtistNameTapped func(artistID string) OnArtistNameTapped func(artistID string)
OnAlbumNameTapped func() OnAlbumNameTapped func()
@@ -37,7 +40,7 @@ type LargeNowPlayingCard struct {
OnSetFavorite func(favorite bool) OnSetFavorite func(favorite bool)
} }
func NewLargeNowPlayingCard() *LargeNowPlayingCard { func NewLargeNowPlayingCard(cfg *backend.Config) *LargeNowPlayingCard {
n := &LargeNowPlayingCard{ n := &LargeNowPlayingCard{
trackName: widget.NewRichTextWithText(""), trackName: widget.NewRichTextWithText(""),
artistName: NewMultiHyperlink(), artistName: NewMultiHyperlink(),
@@ -45,6 +48,7 @@ func NewLargeNowPlayingCard() *LargeNowPlayingCard {
rating: NewStarRating(), rating: NewStarRating(),
favorite: NewFavoriteIcon(), favorite: NewFavoriteIcon(),
cover: NewImagePlaceholder(myTheme.TracksIcon, 300), cover: NewImagePlaceholder(myTheme.TracksIcon, 300),
cfg: cfg,
} }
n.ExtendBaseWidget(n) n.ExtendBaseWidget(n)
n.rating.StarSize = theme.IconInlineSize() + theme.InnerPadding()/2 n.rating.StarSize = theme.IconInlineSize() + theme.InnerPadding()/2
@@ -125,6 +129,7 @@ func (n *LargeNowPlayingCard) Update(item mediaprovider.MediaItem) {
n.trackName.Segments[0].(*widget.TextSegment).Text = "" n.trackName.Segments[0].(*widget.TextSegment).Text = ""
n.artistName.BuildSegments([]string{}, []string{}) n.artistName.BuildSegments([]string{}, []string{})
n.albumName.BuildSegments([]string{}, []string{}) n.albumName.BuildSegments([]string{}, []string{})
n.albumName.Suffix = ""
n.rating.Rating = 0 n.rating.Rating = 0
n.favorite.Favorite = false n.favorite.Favorite = false
n.ratingFavoriteContainer.Hidden = true n.ratingFavoriteContainer.Hidden = true
@@ -143,11 +148,17 @@ func (n *LargeNowPlayingCard) Update(item mediaprovider.MediaItem) {
n.cover.PlaceholderIcon = myTheme.TracksIcon n.cover.PlaceholderIcon = myTheme.TracksIcon
n.ratingFavoriteContainer.Hidden = false n.ratingFavoriteContainer.Hidden = false
n.isRadio = false n.isRadio = false
if y := tr.Year; y != 0 && n.cfg.AlbumsPage.ShowYears {
n.albumName.Suffix = strconv.Itoa(tr.Year)
} else {
n.albumName.Suffix = ""
}
} else if rd, ok := item.(*mediaprovider.RadioStation); ok { } else if rd, ok := item.(*mediaprovider.RadioStation); ok {
n.artistName.BuildSegments([]string{rd.HomePageURL}, []string{rd.HomePageURL}) n.artistName.BuildSegments([]string{rd.HomePageURL}, []string{rd.HomePageURL})
n.ratingFavoriteContainer.Hidden = true n.ratingFavoriteContainer.Hidden = true
n.cover.PlaceholderIcon = myTheme.RadioIcon n.cover.PlaceholderIcon = myTheme.RadioIcon
n.isRadio = true n.isRadio = true
n.albumName.Suffix = ""
} }
n.Refresh() n.Refresh()
+21 -15
View File
@@ -2,9 +2,11 @@ package widgets
import ( import (
"image" "image"
"strconv"
"fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/lang"
"github.com/dweymouth/supersonic/backend"
"github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/mediaprovider"
myTheme "github.com/dweymouth/supersonic/ui/theme" myTheme "github.com/dweymouth/supersonic/ui/theme"
"github.com/dweymouth/supersonic/ui/util" "github.com/dweymouth/supersonic/ui/util"
@@ -15,7 +17,6 @@ import (
"fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
) )
// Shows the current album art, track name, artist name, and album name // Shows the current album art, track name, artist name, and album name
@@ -27,14 +28,15 @@ type NowPlayingCard struct {
trackName *OptionHyperlink trackName *OptionHyperlink
artistName *MultiHyperlink artistName *MultiHyperlink
albumName *ttwidget.Hyperlink albumName *MultiHyperlink
cover *ImagePlaceholder cover *ImagePlaceholder
menu *widget.PopUpMenu menu *widget.PopUpMenu
ratingMenu *fyne.MenuItem ratingMenu *fyne.MenuItem
cfg *backend.Config
OnTrackNameTapped func() OnTrackNameTapped func()
OnArtistNameTapped func(artistID string) OnArtistNameTapped func(artistID string)
OnAlbumNameTapped func() OnAlbumNameTapped func(albumID string)
OnCoverTapped func() OnCoverTapped func()
OnSetRating func(rating int) OnSetRating func(rating int)
OnSetFavorite func(favorite bool) OnSetFavorite func(favorite bool)
@@ -43,11 +45,12 @@ type NowPlayingCard struct {
OnShare func() OnShare func()
} }
func NewNowPlayingCard() *NowPlayingCard { func NewNowPlayingCard(cfg *backend.Config) *NowPlayingCard {
n := &NowPlayingCard{ n := &NowPlayingCard{
trackName: NewOptionHyperlink(), trackName: NewOptionHyperlink(),
artistName: NewMultiHyperlink(), artistName: NewMultiHyperlink(),
albumName: ttwidget.NewHyperlink("", nil), albumName: NewMultiHyperlink(),
cfg: cfg,
} }
n.ExtendBaseWidget(n) n.ExtendBaseWidget(n)
n.cover = NewImagePlaceholder(myTheme.TracksIcon, 85) n.cover = NewImagePlaceholder(myTheme.TracksIcon, 85)
@@ -56,7 +59,6 @@ func NewNowPlayingCard() *NowPlayingCard {
n.cover.Hidden = true n.cover.Hidden = true
n.trackName.Hidden = true n.trackName.Hidden = true
n.albumName.Hidden = true n.albumName.Hidden = true
n.albumName.Truncation = fyne.TextTruncateEllipsis
n.trackName.SetTextStyle(fyne.TextStyle{Bold: true}) n.trackName.SetTextStyle(fyne.TextStyle{Bold: true})
n.trackName.OnShowMenu = n.showMenu n.trackName.OnShowMenu = n.showMenu
n.albumName.OnTapped = n.onAlbumNameTapped n.albumName.OnTapped = n.onAlbumNameTapped
@@ -71,9 +73,9 @@ func (n *NowPlayingCard) MinSize() fyne.Size {
return fyne.NewSize(n.BaseWidget.MinSize().Width, 85) return fyne.NewSize(n.BaseWidget.MinSize().Width, 85)
} }
func (n *NowPlayingCard) onAlbumNameTapped() { func (n *NowPlayingCard) onAlbumNameTapped(albumID string) {
if n.OnAlbumNameTapped != nil { if n.OnAlbumNameTapped != nil {
n.OnAlbumNameTapped() n.OnAlbumNameTapped(albumID)
} }
} }
@@ -138,28 +140,32 @@ func (n *NowPlayingCard) Update(track mediaprovider.MediaItem) {
if track == nil { if track == nil {
n.trackName.SetTextAndToolTip("") n.trackName.SetTextAndToolTip("")
n.artistName.BuildSegments([]string{}, []string{}) n.artistName.BuildSegments([]string{}, []string{})
n.albumName.SetText("") n.albumName.BuildSegments([]string{}, []string{})
n.albumName.SetToolTip("") n.albumName.Suffix = ""
n.cover.Hidden = true n.cover.Hidden = true
} else { } else {
n.cover.Hidden = false n.cover.Hidden = false
n.trackName.SetTextAndToolTip(track.Metadata().Name) n.trackName.SetTextAndToolTip(track.Metadata().Name)
if tr, ok := track.(*mediaprovider.Track); ok { if tr, ok := track.(*mediaprovider.Track); ok {
n.artistName.BuildSegments(tr.ArtistNames, tr.ArtistIDs) n.artistName.BuildSegments(tr.ArtistNames, tr.ArtistIDs)
n.albumName.SetText(tr.Album) n.albumName.BuildSegments([]string{tr.Album}, []string{tr.AlbumID})
n.albumName.SetToolTip(tr.Album) if y := tr.Year; y != 0 && n.cfg.AlbumsPage.ShowYears {
n.albumName.Suffix = strconv.Itoa(tr.Year)
} else {
n.albumName.Suffix = ""
}
n.cover.PlaceholderIcon = myTheme.TracksIcon n.cover.PlaceholderIcon = myTheme.TracksIcon
} else { } else {
n.artistName.BuildSegments([]string{}, []string{}) n.artistName.BuildSegments([]string{}, []string{})
n.albumName.SetText("") n.albumName.BuildSegments([]string{}, []string{})
n.albumName.SetToolTip("") n.albumName.Suffix = ""
n.cover.PlaceholderIcon = myTheme.RadioIcon n.cover.PlaceholderIcon = myTheme.RadioIcon
} }
} }
n.trackName.Hidden = n.trackName.Text() == "" n.trackName.Hidden = n.trackName.Text() == ""
n.trackName.SetMenuBtnEnabled(n.cover.PlaceholderIcon != myTheme.RadioIcon) n.trackName.SetMenuBtnEnabled(n.cover.PlaceholderIcon != myTheme.RadioIcon)
n.artistName.Hidden = len(n.artistName.Segments) == 0 n.artistName.Hidden = len(n.artistName.Segments) == 0
n.albumName.Hidden = n.albumName.Text == "" n.albumName.Hidden = len(n.albumName.Segments) == 0
n.Refresh() n.Refresh()
} }
+4
View File
@@ -457,7 +457,11 @@ func (t *tracklistRowBase) doUpdate(tm *util.TrackListModel, rowNum int) {
t.composer.BuildSegments(tr.ComposerNames, tr.ComposerIDs) t.composer.BuildSegments(tr.ComposerNames, tr.ComposerIDs)
t.genre.BuildSegments(tr.Genres, tr.Genres) t.genre.BuildSegments(tr.Genres, tr.Genres)
t.dur.Text = util.SecondsToMMSS(tr.Duration.Seconds()) t.dur.Text = util.SecondsToMMSS(tr.Duration.Seconds())
if tr.Year != 0 {
t.year.Text = strconv.Itoa(tr.Year) t.year.Text = strconv.Itoa(tr.Year)
} else {
t.year.Text = ""
}
t.plays.Text = strconv.Itoa(int(tr.PlayCount)) t.plays.Text = strconv.Itoa(int(tr.PlayCount))
t.lastPlayed.Text = util.LastPlayedDisplayString(tr.LastPlayed) t.lastPlayed.Text = util.LastPlayedDisplayString(tr.LastPlayed)
t.comment.Text = strings.ReplaceAll(tr.Comment, "\n", " ") t.comment.Text = strings.ReplaceAll(tr.Comment, "\n", " ")