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()
+ })
})