diff --git a/backend/mediaprovider/jellyfin/jellyfinmediaprovider.go b/backend/mediaprovider/jellyfin/jellyfinmediaprovider.go index 4246e04..4b08237 100644 --- a/backend/mediaprovider/jellyfin/jellyfinmediaprovider.go +++ b/backend/mediaprovider/jellyfin/jellyfinmediaprovider.go @@ -312,10 +312,6 @@ func (j *jellyfinMediaProvider) SetFavorite(params mediaprovider.RatingFavoriteP return err } -func (j *jellyfinMediaProvider) SetRating(params mediaprovider.RatingFavoriteParameters, rating int) error { - return errors.New("unimplemented") -} - func (j *jellyfinMediaProvider) GetStreamURL(trackID string, forceRaw bool) (string, error) { return j.client.GetStreamURL(trackID) } diff --git a/backend/mediaprovider/mediaprovider.go b/backend/mediaprovider/mediaprovider.go index ef3e040..25a68e9 100644 --- a/backend/mediaprovider/mediaprovider.go +++ b/backend/mediaprovider/mediaprovider.go @@ -109,8 +109,6 @@ type MediaProvider interface { SetFavorite(params RatingFavoriteParameters, favorite bool) error - SetRating(params RatingFavoriteParameters, rating int) error - GetPlaylists() ([]*Playlist, error) CreatePlaylist(name string, trackIDs []string) error @@ -130,6 +128,10 @@ type MediaProvider interface { RescanLibrary() error } +type SupportsRating interface { + SetRating(params RatingFavoriteParameters, rating int) error +} + func genresMatch(filterGenres, albumGenres []string) bool { for _, g1 := range filterGenres { for _, g2 := range albumGenres { diff --git a/ui/browsing/albumpage.go b/ui/browsing/albumpage.go index bbba005..883aab8 100644 --- a/ui/browsing/albumpage.go +++ b/ui/browsing/albumpage.go @@ -93,6 +93,8 @@ func newAlbumPage( } a.tracklist.SetVisibleColumns(a.cfg.TracklistColumns) a.tracklist.SetSorting(sort) + _, canRate := a.mp.(mediaprovider.SupportsRating) + a.tracklist.Options.DisableRating = !canRate a.tracklist.OnVisibleColumnsChanged = func(cols []string) { a.cfg.TracklistColumns = cols } @@ -154,9 +156,7 @@ func (a *AlbumPage) load() { return } a.header.Update(album, a.im) - a.tracklist.Options = widgets.TracklistOptions{ - ShowDiscNumber: len(album.Tracks) > 0 && album.Tracks[0].DiscNumber != album.Tracks[len(album.Tracks)-1].DiscNumber, - } + a.tracklist.Options.ShowDiscNumber = len(album.Tracks) > 0 && album.Tracks[0].DiscNumber != album.Tracks[len(album.Tracks)-1].DiscNumber a.tracks = album.Tracks a.tracklist.SetTracks(album.Tracks) a.tracklist.SetNowPlaying(a.nowPlayingID) diff --git a/ui/browsing/artistpage.go b/ui/browsing/artistpage.go index 2f08101..1316ebd 100644 --- a/ui/browsing/artistpage.go +++ b/ui/browsing/artistpage.go @@ -229,6 +229,8 @@ func (a *ArtistPage) showTopTracks() { tl = widgets.NewTracklist(ts) } tl.Options = widgets.TracklistOptions{AutoNumber: true} + _, canRate := a.mp.(mediaprovider.SupportsRating) + tl.Options.DisableRating = !canRate tl.SetVisibleColumns(a.cfg.TracklistColumns) tl.SetSorting(a.trackSort) tl.OnVisibleColumnsChanged = func(cols []string) { diff --git a/ui/browsing/favoritespage.go b/ui/browsing/favoritespage.go index c7c3714..8866218 100644 --- a/ui/browsing/favoritespage.go +++ b/ui/browsing/favoritespage.go @@ -370,6 +370,8 @@ func (a *FavoritesPage) onShowFavoriteSongs() { tracklist = widgets.NewTracklist(fav.Tracks) } tracklist.Options = widgets.TracklistOptions{AutoNumber: true} + _, canRate := a.mp.(mediaprovider.SupportsRating) + tracklist.Options.DisableRating = !canRate tracklist.SetVisibleColumns(a.cfg.TracklistColumns) tracklist.SetSorting(a.trackSort) tracklist.OnVisibleColumnsChanged = func(cols []string) { diff --git a/ui/browsing/nowplayingpage.go b/ui/browsing/nowplayingpage.go index 58bbb50..5884c5f 100644 --- a/ui/browsing/nowplayingpage.go +++ b/ui/browsing/nowplayingpage.go @@ -37,11 +37,12 @@ type NowPlayingPage struct { } type nowPlayingPageState struct { - contr *controller.Controller - pool *util.WidgetPool - conf *backend.NowPlayingPageConfig - pm *backend.PlaybackManager - p *player.Player + contr *controller.Controller + pool *util.WidgetPool + conf *backend.NowPlayingPageConfig + pm *backend.PlaybackManager + p *player.Player + canRate bool } func NewNowPlayingPage( @@ -51,9 +52,10 @@ func NewNowPlayingPage( conf *backend.NowPlayingPageConfig, pm *backend.PlaybackManager, p *player.Player, // TODO: once other player backends are supported (eg uPnP), refactor + canRate bool, ) *NowPlayingPage { a := &NowPlayingPage{nowPlayingPageState: nowPlayingPageState{ - contr: contr, pool: pool, conf: conf, pm: pm, p: p, + contr: contr, pool: pool, conf: conf, pm: pm, p: p, canRate: canRate, }} a.ExtendBaseWidget(a) @@ -74,6 +76,7 @@ func NewNowPlayingPage( a.tracklist.Options = widgets.TracklistOptions{ AutoNumber: true, DisablePlaybackMenu: true, + DisableRating: !canRate, AuxiliaryMenuItems: []*fyne.MenuItem{ fyne.NewMenuItem("Remove from queue", a.onRemoveSelectedFromQueue), }, @@ -213,5 +216,5 @@ func (a *NowPlayingPage) load(highlightedTrackID string) { } func (s *nowPlayingPageState) Restore() Page { - return NewNowPlayingPage("", s.contr, s.pool, s.conf, s.pm, s.p) + return NewNowPlayingPage("", s.contr, s.pool, s.conf, s.pm, s.p, s.canRate) } diff --git a/ui/browsing/playlistpage.go b/ui/browsing/playlistpage.go index 19fab2d..6a74f48 100644 --- a/ui/browsing/playlistpage.go +++ b/ui/browsing/playlistpage.go @@ -93,7 +93,9 @@ func newPlaylistPage( fyne.NewMenuItem("Move down", a.onMoveSelectedDown), fyne.NewMenuItem("Move to bottom", a.onMoveSelectedToBottom), }...) + _, canRate := a.sm.Server.(mediaprovider.SupportsRating) a.tracklist.Options = widgets.TracklistOptions{ + DisableRating: !canRate, AuxiliaryMenuItems: []*fyne.MenuItem{reorderMenu, fyne.NewMenuItem("Remove from playlist", a.onRemoveSelectedFromPlaylist)}, } diff --git a/ui/browsing/router.go b/ui/browsing/router.go index 631d6f8..dc2a220 100644 --- a/ui/browsing/router.go +++ b/ui/browsing/router.go @@ -2,6 +2,7 @@ package browsing import ( "github.com/dweymouth/supersonic/backend" + "github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/ui/controller" "github.com/dweymouth/supersonic/ui/util" ) @@ -45,7 +46,8 @@ func (r Router) CreatePage(rte controller.Route) Page { case controller.Genres: return NewGenresPage(r.Controller, r.App.ServerManager.Server) case controller.NowPlaying: - return NewNowPlayingPage(rte.Arg, r.Controller, r.widgetPool, &r.App.Config.NowPlayingPage, r.App.PlaybackManager, r.App.Player) + _, canRate := r.App.ServerManager.Server.(mediaprovider.SupportsRating) + return NewNowPlayingPage(rte.Arg, r.Controller, r.widgetPool, &r.App.Config.NowPlayingPage, r.App.PlaybackManager, r.App.Player, canRate) case controller.Playlist: return NewPlaylistPage(rte.Arg, &r.App.Config.PlaylistPage, r.widgetPool, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager) case controller.Playlists: diff --git a/ui/browsing/trackspage.go b/ui/browsing/trackspage.go index f046ed8..1de70c2 100644 --- a/ui/browsing/trackspage.go +++ b/ui/browsing/trackspage.go @@ -51,8 +51,10 @@ func NewTracksPage(contr *controller.Controller, conf *backend.TracksPageConfig, } else { t.tracklist = widgets.NewTracklist(nil) } + _, canRate := mp.(mediaprovider.SupportsRating) t.tracklist.Options = widgets.TracklistOptions{ DisableSorting: true, + DisableRating: !canRate, AutoNumber: true, } t.tracklist.SetVisibleColumns(conf.TracklistColumns) diff --git a/ui/controller/controller.go b/ui/controller/controller.go index a710ccf..4c3ab4e 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -577,7 +577,11 @@ func (c *Controller) SetTrackFavorites(trackIDs []string, favorite bool) { } func (c *Controller) SetTrackRatings(trackIDs []string, rating int) { - go c.App.ServerManager.Server.SetRating(mediaprovider.RatingFavoriteParameters{ + r, ok := c.App.ServerManager.Server.(mediaprovider.SupportsRating) + if !ok { + return + } + go r.SetRating(mediaprovider.RatingFavoriteParameters{ TrackIDs: trackIDs, }, rating) diff --git a/ui/widgets/starrating.go b/ui/widgets/starrating.go index 8dd7e16..d54814c 100644 --- a/ui/widgets/starrating.go +++ b/ui/widgets/starrating.go @@ -15,15 +15,19 @@ import ( ) var ( - themedResStarFilled = theme.NewThemedResource(res.ResStarFilledSvg) - themedResStarOutline = theme.NewThemedResource(res.ResStarOutlineSvg) + themedResStarFilled = theme.NewThemedResource(res.ResStarFilledSvg) + themedResStarOutline = theme.NewThemedResource(res.ResStarOutlineSvg) + themedDisabledStarOutline = theme.NewDisabledResource(res.ResStarOutlineSvg) ) +var _ fyne.Disableable = (*StarRating)(nil) + type StarRating struct { widget.BaseWidget - Rating int - StarSize float32 + IsDisabled bool + Rating int + StarSize float32 OnRatingChanged func(int) @@ -44,7 +48,9 @@ func (s *StarRating) createContainer() { }) var im *canvas.Image for i := 0; i < 5; i++ { - if s.Rating > i { + if s.IsDisabled { + im = canvas.NewImageFromResource(themedDisabledStarOutline) + } else if s.Rating > i { im = canvas.NewImageFromResource(themedResStarFilled) } else { im = canvas.NewImageFromResource(themedResStarOutline) @@ -61,6 +67,9 @@ func (s *StarRating) MouseIn(e *desktop.MouseEvent) { } func (s *StarRating) MouseMoved(e *desktop.MouseEvent) { + if s.IsDisabled { + return + } hoverRating := int(math.Ceil(5 * float64(e.Position.X/s.Size().Width))) if s.mouseHoverRating != hoverRating { s.holdRating = false @@ -69,7 +78,24 @@ func (s *StarRating) MouseMoved(e *desktop.MouseEvent) { } } +func (s *StarRating) Disable() { + s.IsDisabled = true + s.Refresh() +} + +func (s *StarRating) Enable() { + s.IsDisabled = false + s.Refresh() +} + +func (s *StarRating) Disabled() bool { + return s.IsDisabled +} + func (s *StarRating) MouseOut() { + if s.IsDisabled { + return + } s.mouseHoverRating = 0 s.holdRating = false s.Refresh() @@ -105,7 +131,9 @@ func (s *StarRating) Refresh() { for i := 0; i < 5; i++ { im := s.container.Objects[i].(*canvas.Image) im.SetMinSize(fyne.NewSize(s.StarSize, s.StarSize)) - if rating > i { + if s.IsDisabled { + im.Resource = themedDisabledStarOutline + } else if rating > i { im.Resource = themedResStarFilled } else { im.Resource = themedResStarOutline diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index 47d9f50..bd2e886 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -66,6 +66,9 @@ type TracklistOptions struct { // Disables sorting the tracklist by clicking individual columns. DisableSorting bool + + // Disables the five star rating widget. + DisableRating bool } type Tracklist struct { @@ -732,6 +735,7 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow favorite.OnTapped = t.toggleFavorited t.favorite = container.NewCenter(favorite) t.rating = NewStarRating() + t.rating.IsDisabled = t.tracklist.Options.DisableRating t.rating.StarSize = 16 t.rating.OnRatingChanged = t.setTrackRating t.plays = newTrailingAlignLabel() @@ -842,6 +846,7 @@ func (t *TrackRow) Update(tm *trackModel, rowNum int) { t.dur.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnTime)] t.year.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnYear)] t.favorite.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnFavorite)] + t.rating.IsDisabled = t.tracklist.Options.DisableRating t.rating.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnRating)] t.plays.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnPlays)] t.bitrate.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnBitrate)]