Add config option to globally enable/disable downloads

This commit is contained in:
Deluan
2020-11-10 16:14:43 -05:00
parent bf69c5589f
commit 08e63c867b
9 changed files with 76 additions and 28 deletions
+2
View File
@@ -26,6 +26,7 @@ type configOptions struct {
BaseURL string BaseURL string
UILoginBackgroundURL string UILoginBackgroundURL string
EnableTranscodingConfig bool EnableTranscodingConfig bool
EnableDownloads bool
TranscodingCacheSize string TranscodingCacheSize string
ImageCacheSize string ImageCacheSize string
AutoImportPlaylists bool AutoImportPlaylists bool
@@ -113,6 +114,7 @@ func init() {
viper.SetDefault("transcodingcachesize", "100MB") viper.SetDefault("transcodingcachesize", "100MB")
viper.SetDefault("imagecachesize", "100MB") viper.SetDefault("imagecachesize", "100MB")
viper.SetDefault("autoimportplaylists", true) viper.SetDefault("autoimportplaylists", true)
viper.SetDefault("enabledownloads", true)
// Config options only valid for file/env configuration // Config options only valid for file/env configuration
viper.SetDefault("searchfullstring", false) viper.SetDefault("searchfullstring", false)
+1
View File
@@ -34,6 +34,7 @@ func serveIndex(ds model.DataStore, fs http.FileSystem) http.HandlerFunc {
"welcomeMessage": policy.Sanitize(conf.Server.UIWelcomeMessage), "welcomeMessage": policy.Sanitize(conf.Server.UIWelcomeMessage),
"enableTranscodingConfig": conf.Server.EnableTranscodingConfig, "enableTranscodingConfig": conf.Server.EnableTranscodingConfig,
"gaTrackingId": conf.Server.GATrackingID, "gaTrackingId": conf.Server.GATrackingID,
"enableDownloads": conf.Server.EnableDownloads,
"devActivityMenu": conf.Server.DevActivityMenu, "devActivityMenu": conf.Server.DevActivityMenu,
} }
j, err := json.Marshal(appConfig) j, err := json.Marshal(appConfig)
+11
View File
@@ -103,6 +103,17 @@ var _ = Describe("serveIndex", func() {
Expect(config).To(HaveKeyWithValue("enableTranscodingConfig", true)) Expect(config).To(HaveKeyWithValue("enableTranscodingConfig", true))
}) })
It("sets the enableDownloads", func() {
conf.Server.EnableDownloads = true
r := httptest.NewRequest("GET", "/index.html", nil)
w := httptest.NewRecorder()
serveIndex(ds, fs)(w, r)
config := extractAppConfig(w.Body.String())
Expect(config).To(HaveKeyWithValue("enableDownloads", true))
})
It("sets the gaTrackingId", func() { It("sets the gaTrackingId", func() {
conf.Server.GATrackingID = "UA-12345" conf.Server.GATrackingID = "UA-12345"
r := httptest.NewRequest("GET", "/index.html", nil) r := httptest.NewRequest("GET", "/index.html", nil)
+8
View File
@@ -8,9 +8,11 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/deluan/navidrome/conf"
"github.com/deluan/navidrome/core" "github.com/deluan/navidrome/core"
"github.com/deluan/navidrome/log" "github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model" "github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/model/request"
"github.com/deluan/navidrome/server/subsonic/responses" "github.com/deluan/navidrome/server/subsonic/responses"
"github.com/deluan/navidrome/utils" "github.com/deluan/navidrome/utils"
) )
@@ -80,11 +82,17 @@ func (c *StreamController) Stream(w http.ResponseWriter, r *http.Request) (*resp
func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) { func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
ctx := r.Context() ctx := r.Context()
username, _ := request.UsernameFrom(ctx)
id, err := requiredParamString(r, "id") id, err := requiredParamString(r, "id")
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !conf.Server.EnableDownloads {
log.Warn(ctx, "Downloads are disabled", "user", username, "id", id)
return nil, newError(responses.ErrorAuthorizationFail, "downloads are disabled")
}
entity, err := core.GetEntityByID(ctx, c.ds, id) entity, err := core.GetEntityByID(ctx, c.ds, id)
if err != nil { if err != nil {
return nil, err return nil, err
+12 -9
View File
@@ -15,6 +15,7 @@ import { playNext, addTracks, playTracks, shuffleTracks } from '../actions'
import subsonic from '../subsonic' import subsonic from '../subsonic'
import { formatBytes } from '../common/SizeField' import { formatBytes } from '../common/SizeField'
import { useMediaQuery } from '@material-ui/core' import { useMediaQuery } from '@material-ui/core'
import config from '../config'
const AlbumActions = ({ className, ids, data, record, ...rest }) => { const AlbumActions = ({ className, ids, data, record, ...rest }) => {
const dispatch = useDispatch() const dispatch = useDispatch()
@@ -67,15 +68,17 @@ const AlbumActions = ({ className, ids, data, record, ...rest }) => {
> >
<RiPlayListAddFill /> <RiPlayListAddFill />
</Button> </Button>
<Button {config.enableDownloads && (
onClick={handleDownload} <Button
label={ onClick={handleDownload}
translate('resources.album.actions.download') + label={
(isDesktop ? ` (${formatBytes(record.size)})` : '') translate('resources.album.actions.download') +
} (isDesktop ? ` (${formatBytes(record.size)})` : '')
> }
<CloudDownloadOutlinedIcon /> >
</Button> <CloudDownloadOutlinedIcon />
</Button>
)}
</TopToolbar> </TopToolbar>
) )
} }
+15 -5
View File
@@ -17,6 +17,7 @@ import {
import subsonic from '../subsonic' import subsonic from '../subsonic'
import StarButton from './StarButton' import StarButton from './StarButton'
import { formatBytes } from './SizeField' import { formatBytes } from './SizeField'
import config from '../config'
const useStyles = makeStyles({ const useStyles = makeStyles({
noWrap: { noWrap: {
@@ -45,31 +46,37 @@ const ContextMenu = ({
const options = { const options = {
play: { play: {
enabled: true,
needData: true, needData: true,
label: translate('resources.album.actions.playAll'), label: translate('resources.album.actions.playAll'),
action: (data, ids) => dispatch(playTracks(data, ids)), action: (data, ids) => dispatch(playTracks(data, ids)),
}, },
playNext: { playNext: {
enabled: true,
needData: true, needData: true,
label: translate('resources.album.actions.playNext'), label: translate('resources.album.actions.playNext'),
action: (data, ids) => dispatch(playNext(data, ids)), action: (data, ids) => dispatch(playNext(data, ids)),
}, },
addToQueue: { addToQueue: {
enabled: true,
needData: true, needData: true,
label: translate('resources.album.actions.addToQueue'), label: translate('resources.album.actions.addToQueue'),
action: (data, ids) => dispatch(addTracks(data, ids)), action: (data, ids) => dispatch(addTracks(data, ids)),
}, },
shuffle: { shuffle: {
enabled: true,
needData: true, needData: true,
label: translate('resources.album.actions.shuffle'), label: translate('resources.album.actions.shuffle'),
action: (data, ids) => dispatch(shuffleTracks(data, ids)), action: (data, ids) => dispatch(shuffleTracks(data, ids)),
}, },
addToPlaylist: { addToPlaylist: {
enabled: true,
needData: true, needData: true,
label: translate('resources.album.actions.addToPlaylist'), label: translate('resources.album.actions.addToPlaylist'),
action: (data, ids) => dispatch(openAddToPlaylist({ selectedIds: ids })), action: (data, ids) => dispatch(openAddToPlaylist({ selectedIds: ids })),
}, },
download: { download: {
enabled: config.enableDownloads,
needData: false, needData: false,
label: `${translate('resources.album.actions.download')} (${formatBytes( label: `${translate('resources.album.actions.download')} (${formatBytes(
record.size record.size
@@ -146,11 +153,14 @@ const ContextMenu = ({
open={open} open={open}
onClose={handleOnClose} onClose={handleOnClose}
> >
{Object.keys(options).map((key) => ( {Object.keys(options).map(
<MenuItem value={key} key={key} onClick={handleItemClick}> (key) =>
{options[key].label} options[key].enabled && (
</MenuItem> <MenuItem value={key} key={key} onClick={handleItemClick}>
))} {options[key].label}
</MenuItem>
)
)}
</Menu> </Menu>
</span> </span>
) )
+14 -5
View File
@@ -9,6 +9,7 @@ import { playNext, addTracks, setTrack, openAddToPlaylist } from '../actions'
import subsonic from '../subsonic' import subsonic from '../subsonic'
import StarButton from './StarButton' import StarButton from './StarButton'
import { formatBytes } from './SizeField' import { formatBytes } from './SizeField'
import config from '../config'
const useStyles = makeStyles({ const useStyles = makeStyles({
noWrap: { noWrap: {
@@ -32,18 +33,22 @@ const SongContextMenu = ({
const [anchorEl, setAnchorEl] = useState(null) const [anchorEl, setAnchorEl] = useState(null)
const options = { const options = {
playNow: { playNow: {
enabled: true,
label: translate('resources.song.actions.playNow'), label: translate('resources.song.actions.playNow'),
action: (record) => dispatch(setTrack(record)), action: (record) => dispatch(setTrack(record)),
}, },
playNext: { playNext: {
enabled: true,
label: translate('resources.song.actions.playNext'), label: translate('resources.song.actions.playNext'),
action: (record) => dispatch(playNext({ [record.id]: record })), action: (record) => dispatch(playNext({ [record.id]: record })),
}, },
addToQueue: { addToQueue: {
enabled: true,
label: translate('resources.song.actions.addToQueue'), label: translate('resources.song.actions.addToQueue'),
action: (record) => dispatch(addTracks({ [record.id]: record })), action: (record) => dispatch(addTracks({ [record.id]: record })),
}, },
addToPlaylist: { addToPlaylist: {
enabled: true,
label: translate('resources.song.actions.addToPlaylist'), label: translate('resources.song.actions.addToPlaylist'),
action: (record) => action: (record) =>
dispatch( dispatch(
@@ -54,6 +59,7 @@ const SongContextMenu = ({
), ),
}, },
download: { download: {
enabled: config.enableDownloads,
label: `${translate('resources.song.actions.download')} (${formatBytes( label: `${translate('resources.song.actions.download')} (${formatBytes(
record.size record.size
)})`, )})`,
@@ -95,11 +101,14 @@ const SongContextMenu = ({
open={open} open={open}
onClose={handleClose} onClose={handleClose}
> >
{Object.keys(options).map((key) => ( {Object.keys(options).map(
<MenuItem value={key} key={key} onClick={handleItemClick}> (key) =>
{options[key].label} options[key].enabled && (
</MenuItem> <MenuItem value={key} key={key} onClick={handleItemClick}>
))} {options[key].label}
</MenuItem>
)
)}
</Menu> </Menu>
</span> </span>
) )
+1
View File
@@ -7,6 +7,7 @@ const defaultConfig = {
baseURL: '', baseURL: '',
loginBackgroundURL: 'https://source.unsplash.com/random/1600x900?music', loginBackgroundURL: 'https://source.unsplash.com/random/1600x900?music',
enableTranscodingConfig: true, enableTranscodingConfig: true,
enableDownloads: true,
welcomeMessage: '', welcomeMessage: '',
gaTrackingId: '', gaTrackingId: '',
devActivityMenu: true, devActivityMenu: true,
+12 -9
View File
@@ -18,6 +18,7 @@ import subsonic from '../subsonic'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { formatBytes } from '../common/SizeField' import { formatBytes } from '../common/SizeField'
import { useMediaQuery } from '@material-ui/core' import { useMediaQuery } from '@material-ui/core'
import config from '../config'
const PlaylistActions = ({ className, ids, data, record, ...rest }) => { const PlaylistActions = ({ className, ids, data, record, ...rest }) => {
const dispatch = useDispatch() const dispatch = useDispatch()
@@ -88,15 +89,17 @@ const PlaylistActions = ({ className, ids, data, record, ...rest }) => {
> >
<RiPlayListAddFill /> <RiPlayListAddFill />
</Button> </Button>
<Button {config.enableDownloads && (
onClick={handleDownload} <Button
label={ onClick={handleDownload}
translate('resources.album.actions.download') + label={
(isDesktop ? ` (${formatBytes(record.size)})` : '') translate('resources.album.actions.download') +
} (isDesktop ? ` (${formatBytes(record.size)})` : '')
> }
<CloudDownloadOutlinedIcon /> >
</Button> <CloudDownloadOutlinedIcon />
</Button>
)}
<Button <Button
onClick={handleExport} onClick={handleExport}
label={translate('resources.playlist.actions.export')} label={translate('resources.playlist.actions.export')}