diff --git a/conf/configuration.go b/conf/configuration.go index 665a7992f..bd4f42aba 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -95,7 +95,7 @@ type configOptions struct { UICoverArtSize int EnableReplayGain bool EnableCoverAnimation bool - EnableNowPlaying bool + NowPlaying nowPlayingOptions `json:",omitzero"` UIPlaybackReportInterval time.Duration GATrackingID string EnableLogRedacting bool @@ -236,6 +236,11 @@ type jukeboxOptions struct { AdminOnly bool } +type nowPlayingOptions struct { + Enabled bool + AdminOnly bool +} + type backupOptions struct { Count int Path Dir @@ -332,6 +337,7 @@ func Load(noConfigDump bool) { mapDeprecatedOption("CoverJpegQuality", "CoverArtQuality") mapDeprecatedOption("SimilarSongsMatchThreshold", "Matcher.FuzzyThreshold") mapDeprecatedOption("EnableTranscodingCancellation", "Transcoding.EnableCancellation") + mapDeprecatedOption("EnableNowPlaying", "NowPlaying.Enabled") err := viper.Unmarshal(&Server, viper.DecodeHook( mapstructure.ComposeDecodeHookFunc( @@ -458,6 +464,7 @@ func Load(noConfigDump bool) { logDeprecatedOptions("CoverJpegQuality", "CoverArtQuality") logDeprecatedOptions("SimilarSongsMatchThreshold", "Matcher.FuzzyThreshold") logDeprecatedOptions("EnableTranscodingCancellation", "Transcoding.EnableCancellation") + logDeprecatedOptions("EnableNowPlaying", "NowPlaying.Enabled") // Removed options logRemovedOptions("Spotify.ID", "Spotify.Secret") @@ -789,7 +796,8 @@ func setViperDefaults() { viper.SetDefault("uicoverartsize", consts.DefaultUICoverArtSize) viper.SetDefault("enablereplaygain", true) viper.SetDefault("enablecoveranimation", true) - viper.SetDefault("enablenowplaying", true) + viper.SetDefault("nowplaying.enabled", true) + viper.SetDefault("nowplaying.adminonly", false) viper.SetDefault("uiplaybackreportinterval", consts.DefaultUIPlaybackReportInterval) viper.SetDefault("enableartworkupload", true) viper.SetDefault("maximageuploadsize", consts.DefaultMaxImageUploadSize) diff --git a/core/metrics/insights.go b/core/metrics/insights.go index bcd0343c2..40ff96600 100644 --- a/core/metrics/insights.go +++ b/core/metrics/insights.go @@ -198,7 +198,7 @@ var staticData = sync.OnceValue(func() insights.Data { 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.EnableNowPlaying = conf.Server.NowPlaying.Enabled data.Config.EnableDownloads = conf.Server.EnableDownloads data.Config.EnableSharing = conf.Server.EnableSharing data.Config.EnableStarRating = conf.Server.EnableStarRating diff --git a/core/scrobbler/play_tracker.go b/core/scrobbler/play_tracker.go index 860a80bce..5ff26d6d7 100644 --- a/core/scrobbler/play_tracker.go +++ b/core/scrobbler/play_tracker.go @@ -132,7 +132,7 @@ func newPlayTracker(ds model.DataStore, broker events.Broker, pluginManager Plug prSignal: make(chan struct{}, 1), prWorkerDone: make(chan struct{}), } - enableNowPlaying := conf.Server.EnableNowPlaying + enableNowPlaying := conf.Server.NowPlaying.Enabled m.OnExpiration(func(_ string, info PlaybackSession) { log.Debug("PlaybackSession expired", "clientId", info.PlayerId, "mediaId", info.MediaFile.ID, "state", info.State, "username", info.Username, "userId", info.UserId) @@ -367,7 +367,7 @@ func (p *playTracker) ReportPlayback(ctx context.Context, params ReportPlaybackP p.playMap.Remove(clientId) } - if conf.Server.EnableNowPlaying { + if conf.Server.NowPlaying.Enabled { p.broker.SendBroadcastMessage(ctx, &events.NowPlayingCount{Count: p.playMap.Len()}) } diff --git a/core/scrobbler/play_tracker_test.go b/core/scrobbler/play_tracker_test.go index b5a478c2a..16e057bd7 100644 --- a/core/scrobbler/play_tracker_test.go +++ b/core/scrobbler/play_tracker_test.go @@ -148,7 +148,7 @@ var _ = Describe("PlayTracker", func() { }) It("does not send event when disabled", func() { - conf.Server.EnableNowPlaying = false + conf.Server.NowPlaying.Enabled = false tracker = newPlayTracker(ds, eventBroker, nil) info := PlaybackSession{MediaFile: track, Start: time.Now(), Username: "user"} _ = tracker.playMap.AddWithTTL("player-2", info, 10*time.Millisecond) @@ -455,8 +455,8 @@ var _ = Describe("PlayTracker", func() { Expect(evts[3].(*events.NowPlayingCount).Count).To(Equal(0)) }) - It("does NOT broadcast when EnableNowPlaying is false", func() { - conf.Server.EnableNowPlaying = false + It("does NOT broadcast when NowPlaying is disabled", func() { + conf.Server.NowPlaying.Enabled = false tracker = newPlayTracker(ds, eventBroker, nil) tracker.builtinScrobblers["fake"] = fake diff --git a/server/serve_index.go b/server/serve_index.go index 13fa4a9ce..b2aa25d9e 100644 --- a/server/serve_index.go +++ b/server/serve_index.go @@ -57,7 +57,8 @@ func serveIndex(ds model.DataStore, fs fs.FS, shareInfo *model.Share) http.Handl "uiSearchDebounceMs": conf.Server.UISearchDebounceMs, "uiCoverArtSize": conf.Server.UICoverArtSize, "enableCoverAnimation": conf.Server.EnableCoverAnimation, - "enableNowPlaying": conf.Server.EnableNowPlaying, + "enableNowPlaying": conf.Server.NowPlaying.Enabled, + "nowPlayingAdminOnly": conf.Server.NowPlaying.AdminOnly, "playbackReportIntervalMs": conf.Server.UIPlaybackReportInterval.Milliseconds(), "gaTrackingId": conf.Server.GATrackingID, "losslessFormats": strings.ToUpper(strings.Join(mime.LosslessFormats, ",")), diff --git a/server/serve_index_test.go b/server/serve_index_test.go index 78f3873b8..1ecdb9cff 100644 --- a/server/serve_index_test.go +++ b/server/serve_index_test.go @@ -88,7 +88,8 @@ var _ = Describe("serveIndex", func() { 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("enableNowPlaying", func() { conf.Server.NowPlaying.Enabled = true }, "enableNowPlaying", true), + Entry("nowPlayingAdminOnly", func() { conf.Server.NowPlaying.AdminOnly = true }, "nowPlayingAdminOnly", true), Entry("gaTrackingId", func() { conf.Server.GATrackingID = "UA-12345" }, "gaTrackingId", "UA-12345"), Entry("defaultDownloadableShare", func() { conf.Server.DefaultDownloadableShare = true }, "defaultDownloadableShare", true), Entry("devSidebarPlaylists", func() { conf.Server.DevSidebarPlaylists = true }, "devSidebarPlaylists", true), diff --git a/ui/src/config.js b/ui/src/config.js index 39f0cd467..2643c3a37 100644 --- a/ui/src/config.js +++ b/ui/src/config.js @@ -34,6 +34,7 @@ const defaultConfig = { enableCoverAnimation: true, enableNowPlaying: true, playbackReportIntervalMs: 60000, + nowPlayingAdminOnly: false, devShowArtistPage: true, devUIShowConfig: true, devNewEventStream: false, diff --git a/ui/src/layout/AppBar.jsx b/ui/src/layout/AppBar.jsx index 561701dce..510af7eaa 100644 --- a/ui/src/layout/AppBar.jsx +++ b/ui/src/layout/AppBar.jsx @@ -121,8 +121,10 @@ const CustomUserMenu = ({ onClick, ...rest }) => { return ( <> {config.devActivityPanel && - permissions === 'admin' && - config.enableNowPlaying && } + config.enableNowPlaying && + (!config.nowPlayingAdminOnly || permissions === 'admin') && ( + + )} {config.devActivityPanel && permissions === 'admin' && } diff --git a/ui/src/layout/AppBar.test.jsx b/ui/src/layout/AppBar.test.jsx index f39dd75cb..3b3015f2f 100644 --- a/ui/src/layout/AppBar.test.jsx +++ b/ui/src/layout/AppBar.test.jsx @@ -39,6 +39,7 @@ describe('', () => { beforeEach(() => { config.devActivityPanel = true config.enableNowPlaying = true + config.nowPlayingAdminOnly = true store = createStore(combineReducers({ activity: activityReducer }), { activity: { nowPlayingCount: 0 }, }) @@ -62,4 +63,14 @@ describe('', () => { ) expect(screen.queryByTestId('now-playing-panel')).toBeNull() }) + + it('shows NowPlayingPanel to all users when adminOnly is false', () => { + config.nowPlayingAdminOnly = false + render( + + + , + ) + expect(screen.getByTestId('now-playing-panel')).toBeInTheDocument() + }) })