Add config option to globally enable/disable downloads
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,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
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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')}
|
||||||
|
|||||||
Reference in New Issue
Block a user