diff --git a/core/archiver.go b/core/archiver.go
index 6e2a48844..cc1621de4 100644
--- a/core/archiver.go
+++ b/core/archiver.go
@@ -91,8 +91,10 @@ func (a *archiver) albumFilename(mf model.MediaFile, format string, isMultDisc b
func (a *archiver) ZipShare(ctx context.Context, id string, out io.Writer) error {
s, err := a.shares.Load(ctx, id)
+ if !s.Downloadable {
+ return model.ErrNotAuthorized
+ }
if err != nil {
- log.Error(ctx, "Error loading mediafiles from share", "id", id, err)
return err
}
log.Debug(ctx, "Zipping share", "name", s.ID, "format", s.Format, "bitrate", s.MaxBitRate, "numTracks", len(s.Tracks))
diff --git a/server/public/handle_shares.go b/server/public/handle_shares.go
index 89dde7713..aa09cfa17 100644
--- a/server/public/handle_shares.go
+++ b/server/public/handle_shares.go
@@ -45,6 +45,9 @@ func checkShareError(ctx context.Context, w http.ResponseWriter, err error, id s
case errors.Is(err, model.ErrNotFound):
log.Error(ctx, "Share not found", "id", id, err)
http.Error(w, "Share not found", http.StatusNotFound)
+ case errors.Is(err, model.ErrNotAuthorized):
+ log.Error(ctx, "Share is not downloadable", "id", id, err)
+ http.Error(w, "This share is not downloadable", http.StatusForbidden)
case err != nil:
log.Error(ctx, "Error retrieving share", "id", id, err)
http.Error(w, "Error retrieving share", http.StatusInternalServerError)
diff --git a/server/serve_index.go b/server/serve_index.go
index 192b8257e..cb4271b4e 100644
--- a/server/serve_index.go
+++ b/server/serve_index.go
@@ -122,8 +122,10 @@ func getIndexTemplate(r *http.Request, fs fs.FS) (*template.Template, error) {
}
type shareData struct {
- Description string `json:"description"`
- Tracks []shareTrack `json:"tracks"`
+ ID string `json:"id"`
+ Description string `json:"description"`
+ Downloadable bool `json:"downloadable"`
+ Tracks []shareTrack `json:"tracks"`
}
type shareTrack struct {
@@ -141,7 +143,9 @@ func addShareData(r *http.Request, data map[string]interface{}, shareInfo *model
return
}
sd := shareData{
- Description: shareInfo.Description,
+ ID: shareInfo.ID,
+ Description: shareInfo.Description,
+ Downloadable: shareInfo.Downloadable,
}
sd.Tracks = slice.Map(shareInfo.Tracks, func(mf model.MediaFile) shareTrack {
return shareTrack{
diff --git a/ui/src/App.js b/ui/src/App.js
index 6a876afe8..6d1853349 100644
--- a/ui/src/App.js
+++ b/ui/src/App.js
@@ -37,7 +37,7 @@ import config, { shareInfo } from './config'
import { setDispatch, startEventStream, stopEventStream } from './eventStream'
import { keyMap } from './hotkeys'
import useChangeThemeColor from './useChangeThemeColor'
-import SharePlayer from './SharePlayer'
+import SharePlayer from './share/SharePlayer'
const history = createHashHistory()
diff --git a/ui/src/SharePlayer.js b/ui/src/share/SharePlayer.js
similarity index 73%
rename from ui/src/SharePlayer.js
rename to ui/src/share/SharePlayer.js
index 9410fbc23..387b6fcd4 100644
--- a/ui/src/SharePlayer.js
+++ b/ui/src/share/SharePlayer.js
@@ -1,6 +1,6 @@
import ReactJkMusicPlayer from 'navidrome-music-player'
-import { shareInfo } from './config'
-import { shareCoverUrl, shareStreamUrl } from './utils'
+import { shareInfo } from '../config'
+import { shareCoverUrl, shareDownloadUrl, shareStreamUrl } from '../utils'
import { makeStyles } from '@material-ui/core/styles'
@@ -34,12 +34,17 @@ const SharePlayer = () => {
duration: s.duration,
}
})
+ const onBeforeAudioDownload = () => {
+ return Promise.resolve({
+ src: shareDownloadUrl(shareInfo?.id),
+ })
+ }
const options = {
audioLists: list,
mode: 'full',
toggleMode: false,
mobileMediaQuery: '',
- showDownload: false,
+ showDownload: shareInfo?.downloadable,
showReload: false,
showMediaSession: true,
theme: 'auto',
@@ -49,7 +54,13 @@ const SharePlayer = () => {
spaceBar: true,
volumeFade: { fadeIn: 200, fadeOut: 200 },
}
- return
+ return (
+
+ )
}
export default SharePlayer
diff --git a/ui/src/utils/urls.js b/ui/src/utils/urls.js
index aba22f472..fe6c19c6f 100644
--- a/ui/src/utils/urls.js
+++ b/ui/src/utils/urls.js
@@ -19,6 +19,10 @@ export const shareStreamUrl = (id) => {
return baseUrl(config.publicBaseUrl + '/s/' + id)
}
+export const shareDownloadUrl = (id) => {
+ return baseUrl(config.publicBaseUrl + '/d/' + id)
+}
+
export const shareCoverUrl = (id) => {
return baseUrl(config.publicBaseUrl + '/img/' + id + '?size=300')
}