diff --git a/src/components/itemContextMenu.js b/src/components/itemContextMenu.js index 15980b23ad..e1c1545b69 100644 --- a/src/components/itemContextMenu.js +++ b/src/components/itemContextMenu.js @@ -141,6 +141,14 @@ import toast from './toast/toast'; }); } + if (item.Type === 'Season' || item.Type == 'Series') { + commands.push({ + name: globalize.translate('DownloadAll'), + id: 'downloadall', + icon: 'file_download' + }); + } + if (item.CanDelete && options.deleteItem !== false) { if (item.Type === 'Playlist' || item.Type === 'BoxSet') { commands.push({ @@ -316,6 +324,7 @@ import toast from './toast/toast'; const apiClient = ServerConnections.getApiClient(serverId); return new Promise(function (resolve, reject) { + // eslint-disable-next-line sonarjs/max-switch-cases switch (id) { case 'addtocollection': import('./collectionEditor/collectionEditor').then(({default: CollectionEditor}) => { @@ -347,6 +356,48 @@ import toast from './toast/toast'; getResolveFunction(getResolveFunction(resolve, id), id)(); }); break; + case 'downloadall': { + const downloadEpisodes = episodes => { + import('../scripts/fileDownloader').then((fileDownloader) => { + const downloads = episodes.map(episode => { + const downloadHref = apiClient.getItemDownloadUrl(episode.Id); + return { + url: downloadHref, + itemId: episode.Id, + serverId: serverId, + title: episode.Name, + filename: episode.Path.replace(/^.*[\\/]/, '') + }; + }); + + fileDownloader.download(downloads); + }); + }; + const downloadSeasons = seasons => { + Promise.all(seasons.map(seasonItem => { + return apiClient.getEpisodes(seasonItem.SeriesId, { + seasonId: seasonItem.Id, + userId: options.user.Id, + Fields: 'CanDownload,Path' + }); + } + )).then(seasonData => { + downloadEpisodes(seasonData.map(season => season.Items).flat()); + }); + }; + + if (item.Type === 'Season') { + downloadSeasons([item]); + } else if (item.Type === 'Series') { + apiClient.getSeasons(item.Id, { + userId: options.user.Id, + Fields: 'ItemCounts' + }).then(seasons => downloadSeasons(seasons.Items)); + } + + getResolveFunction(getResolveFunction(resolve, id), id)(); + break; + } case 'copy-stream': { const downloadHref = apiClient.getItemDownloadUrl(itemId); copy(downloadHref).then(() => { diff --git a/src/scripts/shell.js b/src/scripts/shell.js index b21d658f9f..cbcd9f8859 100644 --- a/src/scripts/shell.js +++ b/src/scripts/shell.js @@ -42,6 +42,10 @@ export default { * @returns true on success */ downloadFiles(items) { + if (window.NativeShell?.downloadFiles) { + window.NativeShell.downloadFiles(items); + return true; + } if (window.NativeShell?.downloadFile) { items.forEach(item => { window.NativeShell.downloadFile(item); diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 906a76f1f5..aa21e8fc1c 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -208,6 +208,7 @@ "DoNotRecord": "Do not record", "Down": "Down", "Download": "Download", + "DownloadAll": "Download All", "DownloadsValue": "{0} downloads", "DrmChannelsNotImported": "Channels with DRM will not be imported.", "DropShadow": "Drop Shadow",