diff --git a/resources/i18n/pt.json b/resources/i18n/pt.json
index b313d60ed..f1c1fcf09 100644
--- a/resources/i18n/pt.json
+++ b/resources/i18n/pt.json
@@ -116,7 +116,8 @@
},
"actions": {
"selectPlaylist": "Selecione a playlist:",
- "addNewPlaylist": "Criar \"%{name}\""
+ "addNewPlaylist": "Criar \"%{name}\"",
+ "export": "Exportar"
}
}
},
diff --git a/server/app/playlists.go b/server/app/playlists.go
index 6aa4747b1..a480118ca 100644
--- a/server/app/playlists.go
+++ b/server/app/playlists.go
@@ -56,16 +56,24 @@ func handleExportPlaylist(ds model.DataStore) http.HandlerFunc {
return
}
+ log.Debug(ctx, "Exporting playlist as M3U", "playlistId", plsId, "name", pls.Name)
w.Header().Set("Content-Type", "audio/x-mpegurl")
+ disposition := fmt.Sprintf("attachment; filename=\"%s.m3u\"", pls.Name)
+ w.Header().Set("Content-Disposition", disposition)
// TODO: Move this and the import playlist logic to `core`
- w.Write([]byte("#EXTM3U\n"))
+ _, err = w.Write([]byte("#EXTM3U\n"))
+ if err != nil {
+ log.Error(ctx, "Error sending playlist", "name", pls.Name)
+ return
+ }
for _, t := range pls.Tracks {
header := fmt.Sprintf("#EXTINF:%.f,%s - %s\n", t.Duration, t.Artist, t.Title)
line := t.Path + "\n"
- _, err := w.Write([]byte(header + line))
+ _, err = w.Write([]byte(header + line))
if err != nil {
log.Error(ctx, "Error sending playlist", "name", pls.Name)
+ return
}
}
}
diff --git a/ui/src/consts.js b/ui/src/consts.js
new file mode 100644
index 000000000..77d107519
--- /dev/null
+++ b/ui/src/consts.js
@@ -0,0 +1,3 @@
+export const REST_URL = '/app/api'
+
+export const M3U_MIME_TYPE = 'audio/x-mpegurl'
diff --git a/ui/src/dataProvider/index.js b/ui/src/dataProvider/index.js
index 6828ab2c5..e9ceea761 100644
--- a/ui/src/dataProvider/index.js
+++ b/ui/src/dataProvider/index.js
@@ -1,3 +1,6 @@
+import httpClient from './httpClient'
import wrapperDataProvider from './wrapperDataProvider'
+export { httpClient }
+
export default wrapperDataProvider
diff --git a/ui/src/dataProvider/wrapperDataProvider.js b/ui/src/dataProvider/wrapperDataProvider.js
index c7ea249ed..827ed1c85 100644
--- a/ui/src/dataProvider/wrapperDataProvider.js
+++ b/ui/src/dataProvider/wrapperDataProvider.js
@@ -1,9 +1,8 @@
import jsonServerProvider from 'ra-data-json-server'
import httpClient from './httpClient'
+import { REST_URL } from '../consts'
-const restUrl = '/app/api'
-
-const dataProvider = jsonServerProvider(restUrl, httpClient)
+const dataProvider = jsonServerProvider(REST_URL, httpClient)
const mapResource = (resource, params) => {
switch (resource) {
diff --git a/ui/src/i18n/en.json b/ui/src/i18n/en.json
index f0e0a03be..f8618f297 100644
--- a/ui/src/i18n/en.json
+++ b/ui/src/i18n/en.json
@@ -85,7 +85,8 @@
},
"actions": {
"selectPlaylist": "Select a playlist:",
- "addNewPlaylist": "Create \"%{name}\""
+ "addNewPlaylist": "Create \"%{name}\"",
+ "export": "Export"
}
},
"user": {
diff --git a/ui/src/playlist/PlaylistActions.js b/ui/src/playlist/PlaylistActions.js
index 803ff9677..c82de43da 100644
--- a/ui/src/playlist/PlaylistActions.js
+++ b/ui/src/playlist/PlaylistActions.js
@@ -9,7 +9,10 @@ import {
import PlayArrowIcon from '@material-ui/icons/PlayArrow'
import ShuffleIcon from '@material-ui/icons/Shuffle'
import CloudDownloadOutlinedIcon from '@material-ui/icons/CloudDownloadOutlined'
+import QueueMusicIcon from '@material-ui/icons/QueueMusic'
+import { httpClient } from '../dataProvider'
import { playTracks, shuffleTracks } from '../audioplayer'
+import { M3U_MIME_TYPE, REST_URL } from '../consts'
import subsonic from '../subsonic'
const PlaylistActions = ({
@@ -18,38 +21,68 @@ const PlaylistActions = ({
data,
exporter,
permanentFilter,
- playlistId,
+ record,
...rest
}) => {
const dispatch = useDispatch()
const translate = useTranslate()
+ const handlePlay = React.useCallback(() => {
+ dispatch(playTracks(data, ids))
+ }, [dispatch, data, ids])
+
+ const handleShuffle = React.useCallback(() => {
+ dispatch(shuffleTracks(data, ids))
+ }, [dispatch, data, ids])
+
+ const handleDownload = React.useCallback(() => {
+ subsonic.download(record.id)
+ }, [record])
+
+ const handleExport = React.useCallback(
+ () =>
+ httpClient(`${REST_URL}/playlist/${record.id}/tracks`, {
+ headers: new Headers({ Accept: M3U_MIME_TYPE }),
+ }).then((res) => {
+ console.log(res)
+ const blob = new Blob([res.body], { type: M3U_MIME_TYPE })
+ const url = window.URL.createObjectURL(blob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `${record.name}.m3u`
+ document.body.appendChild(link)
+ link.click()
+ link.parentNode.removeChild(link)
+ }),
+ [record]
+ )
+
return (