diff --git a/frontend/src/InteractiveImport/Album/SelectAlbumModalContentConnector.js b/frontend/src/InteractiveImport/Album/SelectAlbumModalContentConnector.js
index 27d213d59..12cd88e53 100644
--- a/frontend/src/InteractiveImport/Album/SelectAlbumModalContentConnector.js
+++ b/frontend/src/InteractiveImport/Album/SelectAlbumModalContentConnector.js
@@ -70,7 +70,7 @@ class SelectAlbumModalContentConnector extends Component {
});
});
- this.props.saveInteractiveImportItem({ id: ids });
+ this.props.saveInteractiveImportItem({ ids });
this.props.onModalClose(true);
};
diff --git a/frontend/src/InteractiveImport/AlbumRelease/SelectAlbumReleaseModalContentConnector.js b/frontend/src/InteractiveImport/AlbumRelease/SelectAlbumReleaseModalContentConnector.js
index ad211684e..427592afb 100644
--- a/frontend/src/InteractiveImport/AlbumRelease/SelectAlbumReleaseModalContentConnector.js
+++ b/frontend/src/InteractiveImport/AlbumRelease/SelectAlbumReleaseModalContentConnector.js
@@ -37,7 +37,7 @@ class SelectAlbumReleaseModalContentConnector extends Component {
});
});
- this.props.saveInteractiveImportItem({ id: ids });
+ this.props.saveInteractiveImportItem({ ids });
this.props.onModalClose(true);
};
diff --git a/frontend/src/InteractiveImport/Artist/SelectArtistModalContentConnector.js b/frontend/src/InteractiveImport/Artist/SelectArtistModalContentConnector.js
index 77fa0fda8..c2cca6c27 100644
--- a/frontend/src/InteractiveImport/Artist/SelectArtistModalContentConnector.js
+++ b/frontend/src/InteractiveImport/Artist/SelectArtistModalContentConnector.js
@@ -54,7 +54,7 @@ class SelectArtistModalContentConnector extends Component {
});
});
- this.props.saveInteractiveImportItem({ id: ids });
+ this.props.saveInteractiveImportItem({ ids });
this.props.onModalClose(true);
};
diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js
index be555d969..f9ee3c59e 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js
@@ -21,6 +21,7 @@ import SelectAlbumReleaseModal from 'InteractiveImport/AlbumRelease/SelectAlbumR
import SelectArtistModal from 'InteractiveImport/Artist/SelectArtistModal';
import ConfirmImportModal from 'InteractiveImport/Confirmation/ConfirmImportModal';
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
+import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
@@ -52,6 +53,11 @@ const columns = [
label: translate('Tracks'),
isVisible: true
},
+ {
+ name: 'releaseGroup',
+ label: translate('ReleaseGroup'),
+ isVisible: true
+ },
{
name: 'quality',
label: translate('Quality'),
@@ -81,6 +87,7 @@ const filterExistingFilesOptions = {
};
const importModeOptions = [
+ { key: 'chooseImportMode', value: translate('ChooseImportMethod'), disabled: true },
{ key: 'move', value: translate('MoveFiles') },
{ key: 'copy', value: translate('HardlinkCopyFiles') }
];
@@ -89,6 +96,7 @@ const SELECT = 'select';
const ARTIST = 'artist';
const ALBUM = 'album';
const ALBUM_RELEASE = 'albumRelease';
+const RELEASE_GROUP = 'releaseGroup';
const QUALITY = 'quality';
const replaceExistingFilesOptions = {
@@ -292,7 +300,8 @@ class InteractiveImportModalContent extends Component {
{ key: SELECT, value: translate('Select...'), disabled: true },
{ key: ALBUM, value: translate('SelectAlbum') },
{ key: ALBUM_RELEASE, value: translate('SelectAlbumRelease') },
- { key: QUALITY, value: translate('SelectQuality') }
+ { key: QUALITY, value: translate('SelectQuality') },
+ { key: RELEASE_GROUP, value: translate('SelectReleaseGroup') }
];
if (allowArtistChange) {
@@ -513,6 +522,13 @@ class InteractiveImportModalContent extends Component {
onModalClose={this.onSelectModalClose}
/>
+
+
{
const files = [];
+ if (importMode === 'chooseImportMethod') {
+ this.setState({ interactiveImportErrorMessage: 'An import mode must be selected' });
+ return;
+ }
+
_.forEach(this.props.items, (item) => {
const isSelected = selected.indexOf(item.id) > -1;
diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css
index d5286412c..599225f02 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css
@@ -16,10 +16,11 @@
cursor: pointer;
}
-.loading {
+.reprocessing {
composes: loading from '~Components/Loading/LoadingIndicator.css';
margin-top: 0;
+ text-align: start;
}
.additionalFile {
diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js
index c994a3343..49d3fd0da 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js
@@ -13,6 +13,7 @@ import { icons, kinds, sortDirections, tooltipPositions } from 'Helpers/Props';
import SelectAlbumModal from 'InteractiveImport/Album/SelectAlbumModal';
import SelectArtistModal from 'InteractiveImport/Artist/SelectArtistModal';
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
+import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
import SelectTrackModal from 'InteractiveImport/Track/SelectTrackModal';
import formatBytes from 'Utilities/Number/formatBytes';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
@@ -32,6 +33,7 @@ class InteractiveImportRow extends Component {
isSelectArtistModalOpen: false,
isSelectAlbumModalOpen: false,
isSelectTrackModalOpen: false,
+ isSelectReleaseGroupModalOpen: false,
isSelectQualityModalOpen: false
};
}
@@ -119,6 +121,10 @@ class InteractiveImportRow extends Component {
this.setState({ isSelectTrackModalOpen: true });
};
+ onSelectReleaseGroupPress = () => {
+ this.setState({ isSelectReleaseGroupModalOpen: true });
+ };
+
onSelectQualityPress = () => {
this.setState({ isSelectQualityModalOpen: true });
};
@@ -138,6 +144,11 @@ class InteractiveImportRow extends Component {
this.selectRowAfterChange(changed);
};
+ onSelectReleaseGroupModalClose = (changed) => {
+ this.setState({ isSelectReleaseGroupModalOpen: false });
+ this.selectRowAfterChange(changed);
+ };
+
onSelectQualityModalClose = (changed) => {
this.setState({ isSelectQualityModalOpen: false });
this.selectRowAfterChange(changed);
@@ -156,12 +167,13 @@ class InteractiveImportRow extends Component {
albumReleaseId,
tracks,
quality,
+ releaseGroup,
size,
rejections,
+ isReprocessing,
audioTags,
additionalFile,
isSelected,
- isSaving,
onSelectedChange
} = this.props;
@@ -169,6 +181,7 @@ class InteractiveImportRow extends Component {
isSelectArtistModalOpen,
isSelectAlbumModalOpen,
isSelectTrackModalOpen,
+ isSelectReleaseGroupModalOpen,
isSelectQualityModalOpen
} = this.state;
@@ -185,8 +198,9 @@ class InteractiveImportRow extends Component {
const showArtistPlaceholder = isSelected && !artist;
const showAlbumNumberPlaceholder = isSelected && !!artist && !album;
- const showTrackNumbersPlaceholder = !isSaving && isSelected && !!album && !tracks.length;
- const showTrackNumbersLoading = isSaving && !tracks.length;
+ const showTrackNumbersPlaceholder = !isReprocessing && isSelected && !!album && !tracks.length;
+ const showTrackNumbersLoading = isReprocessing && !tracks.length;
+ const showReleaseGroupPlaceholder = isSelected && !releaseGroup;
const showQualityPlaceholder = isSelected && !quality;
const pathCellContents = (
@@ -253,6 +267,17 @@ class InteractiveImportRow extends Component {
}
+
+ {
+ showReleaseGroupPlaceholder ?
+ :
+ releaseGroup
+ }
+
+
}
position={tooltipPositions.LEFT}
+ canFlip={false}
/> :
null
}
@@ -333,6 +359,13 @@ class InteractiveImportRow extends Component {
onModalClose={this.onSelectTrackModalClose}
/>
+
+
@@ -73,7 +75,12 @@ InteractiveImportModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
folder: PropTypes.string,
downloadId: PropTypes.string,
+ modalTitle: PropTypes.string.isRequired,
onModalClose: PropTypes.func.isRequired
};
+InteractiveImportModal.defaultProps = {
+ modalTitle: 'Manual Import'
+};
+
export default InteractiveImportModal;
diff --git a/frontend/src/InteractiveImport/Quality/SelectQualityModalContentConnector.js b/frontend/src/InteractiveImport/Quality/SelectQualityModalContentConnector.js
index fca7e58a8..0124969a0 100644
--- a/frontend/src/InteractiveImport/Quality/SelectQualityModalContentConnector.js
+++ b/frontend/src/InteractiveImport/Quality/SelectQualityModalContentConnector.js
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
-import { updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
+import { saveInteractiveImportItem, updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
import { fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
import getQualities from 'Utilities/Quality/getQualities';
import SelectQualityModalContent from './SelectQualityModalContent';
@@ -31,7 +31,8 @@ function createMapStateToProps() {
const mapDispatchToProps = {
dispatchFetchQualityProfileSchema: fetchQualityProfileSchema,
- dispatchUpdateInteractiveImportItems: updateInteractiveImportItems
+ dispatchUpdateInteractiveImportItems: updateInteractiveImportItems,
+ dispatchSaveInteractiveImportItems: saveInteractiveImportItem
};
class SelectQualityModalContentConnector extends Component {
@@ -49,6 +50,12 @@ class SelectQualityModalContentConnector extends Component {
// Listeners
onQualitySelect = ({ qualityId, proper, real }) => {
+ const {
+ ids,
+ dispatchUpdateInteractiveImportItems,
+ dispatchSaveInteractiveImportItems
+ } = this.props;
+
const quality = _.find(this.props.items,
(item) => item.id === qualityId);
@@ -57,14 +64,16 @@ class SelectQualityModalContentConnector extends Component {
real: real ? 1 : 0
};
- this.props.dispatchUpdateInteractiveImportItems({
- ids: this.props.ids,
+ dispatchUpdateInteractiveImportItems({
+ ids,
quality: {
quality,
revision
}
});
+ dispatchSaveInteractiveImportItems({ ids });
+
this.props.onModalClose(true);
};
@@ -89,6 +98,7 @@ SelectQualityModalContentConnector.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
dispatchFetchQualityProfileSchema: PropTypes.func.isRequired,
dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
+ dispatchSaveInteractiveImportItems: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
diff --git a/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModal.js b/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModal.js
new file mode 100644
index 000000000..04f6e6af3
--- /dev/null
+++ b/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModal.js
@@ -0,0 +1,37 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import Modal from 'Components/Modal/Modal';
+import SelectReleaseGroupModalContentConnector from './SelectReleaseGroupModalContentConnector';
+
+class SelectReleaseGroupModal extends Component {
+
+ //
+ // Render
+
+ render() {
+ const {
+ isOpen,
+ onModalClose,
+ ...otherProps
+ } = this.props;
+
+ return (
+
+
+
+ );
+ }
+}
+
+SelectReleaseGroupModal.propTypes = {
+ isOpen: PropTypes.bool.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default SelectReleaseGroupModal;
diff --git a/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModalContent.css b/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModalContent.css
new file mode 100644
index 000000000..72dfb1cb6
--- /dev/null
+++ b/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModalContent.css
@@ -0,0 +1,7 @@
+.modalBody {
+ composes: modalBody from '~Components/Modal/ModalBody.css';
+
+ display: flex;
+ flex: 1 1 auto;
+ flex-direction: column;
+}
diff --git a/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModalContent.js b/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModalContent.js
new file mode 100644
index 000000000..91a9303e2
--- /dev/null
+++ b/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModalContent.js
@@ -0,0 +1,103 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import Form from 'Components/Form/Form';
+import FormGroup from 'Components/Form/FormGroup';
+import FormInputGroup from 'Components/Form/FormInputGroup';
+import FormLabel from 'Components/Form/FormLabel';
+import Button from 'Components/Link/Button';
+import ModalBody from 'Components/Modal/ModalBody';
+import ModalContent from 'Components/Modal/ModalContent';
+import ModalFooter from 'Components/Modal/ModalFooter';
+import ModalHeader from 'Components/Modal/ModalHeader';
+import { inputTypes, kinds, scrollDirections } from 'Helpers/Props';
+import styles from './SelectReleaseGroupModalContent.css';
+
+class SelectReleaseGroupModalContent extends Component {
+
+ //
+ // Lifecycle
+
+ constructor(props, context) {
+ super(props, context);
+
+ const {
+ releaseGroup
+ } = props;
+
+ this.state = {
+ releaseGroup
+ };
+ }
+
+ //
+ // Listeners
+
+ onReleaseGroupChange = ({ value }) => {
+ this.setState({ releaseGroup: value });
+ };
+
+ onReleaseGroupSelect = () => {
+ this.props.onReleaseGroupSelect(this.state);
+ };
+
+ //
+ // Render
+
+ render() {
+ const {
+ onModalClose
+ } = this.props;
+
+ const {
+ releaseGroup
+ } = this.state;
+
+ return (
+
+
+ Manual Import - Set Release Group
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+SelectReleaseGroupModalContent.propTypes = {
+ releaseGroup: PropTypes.string.isRequired,
+ onReleaseGroupSelect: PropTypes.func.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default SelectReleaseGroupModalContent;
diff --git a/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModalContentConnector.js b/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModalContentConnector.js
new file mode 100644
index 000000000..4f05afbb4
--- /dev/null
+++ b/frontend/src/InteractiveImport/ReleaseGroup/SelectReleaseGroupModalContentConnector.js
@@ -0,0 +1,54 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { saveInteractiveImportItem, updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
+import SelectReleaseGroupModalContent from './SelectReleaseGroupModalContent';
+
+const mapDispatchToProps = {
+ dispatchUpdateInteractiveImportItems: updateInteractiveImportItems,
+ dispatchSaveInteractiveImportItems: saveInteractiveImportItem
+};
+
+class SelectReleaseGroupModalContentConnector extends Component {
+
+ //
+ // Listeners
+
+ onReleaseGroupSelect = ({ releaseGroup }) => {
+ const {
+ ids,
+ dispatchUpdateInteractiveImportItems,
+ dispatchSaveInteractiveImportItems
+ } = this.props;
+
+ dispatchUpdateInteractiveImportItems({
+ ids,
+ releaseGroup
+ });
+
+ dispatchSaveInteractiveImportItems({ ids });
+
+ this.props.onModalClose(true);
+ };
+
+ //
+ // Render
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+SelectReleaseGroupModalContentConnector.propTypes = {
+ ids: PropTypes.arrayOf(PropTypes.number).isRequired,
+ dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
+ dispatchSaveInteractiveImportItems: PropTypes.func.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default connect(null, mapDispatchToProps)(SelectReleaseGroupModalContentConnector);
diff --git a/frontend/src/Store/Actions/interactiveImportActions.js b/frontend/src/Store/Actions/interactiveImportActions.js
index 663b98abc..86cf06c5e 100644
--- a/frontend/src/Store/Actions/interactiveImportActions.js
+++ b/frontend/src/Store/Actions/interactiveImportActions.js
@@ -5,10 +5,9 @@ import { sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import updateSectionState from 'Utilities/State/updateSectionState';
-import { set, update } from './baseActions';
+import { set, update, updateItem } from './baseActions';
import createFetchHandler from './Creators/createFetchHandler';
import createHandleActions from './Creators/createHandleActions';
-import createSaveProviderHandler from './Creators/createSaveProviderHandler';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
//
@@ -18,6 +17,8 @@ export const section = 'interactiveImport';
const albumsSection = `${section}.albums`;
const trackFilesSection = `${section}.trackFiles`;
+let abortCurrentRequest = null;
+let currentIds = [];
const MAXIMUM_RECENT_FOLDERS = 10;
@@ -34,7 +35,7 @@ export const defaultState = {
sortKey: 'path',
sortDirection: sortDirections.ASCENDING,
recentFolders: [],
- importMode: 'move',
+ importMode: 'chooseImportMode',
sortPredicates: {
path: function(item, direction) {
const path = item.path;
@@ -156,7 +157,83 @@ export const actionHandlers = handleThunks({
});
},
- [SAVE_INTERACTIVE_IMPORT_ITEM]: createSaveProviderHandler(section, '/manualimport', {}, true),
+ [SAVE_INTERACTIVE_IMPORT_ITEM]: function(getState, payload, dispatch) {
+ if (abortCurrentRequest) {
+ abortCurrentRequest();
+ }
+
+ dispatch(batchActions([
+ ...currentIds.map((id) => updateItem({
+ section,
+ id,
+ isReprocessing: false,
+ updateOnly: true
+ })),
+ ...payload.ids.map((id) => updateItem({
+ section,
+ id,
+ isReprocessing: true,
+ updateOnly: true
+ }))
+ ]));
+
+ const items = getState()[section].items;
+
+ const requestPayload = payload.ids.map((id) => {
+ const item = items.find((i) => i.id === id);
+
+ return {
+ id,
+ path: item.path,
+ artistId: item.artist ? item.artist.id : undefined,
+ albumId: item.album ? item.album.id : undefined,
+ albumReleaseId: item.albumReleaseId ? item.albumReleaseId : undefined,
+ trackIds: (item.tracks || []).map((e) => e.id),
+ quality: item.quality,
+ releaseGroup: item.releaseGroup,
+ downloadId: item.downloadId,
+ additionalFile: item.additionalFile,
+ replaceExistingFiles: item.replaceExistingFiles,
+ disableReleaseSwitching: item.disableReleaseSwitching
+ };
+ });
+
+ const { request, abortRequest } = createAjaxRequest({
+ method: 'POST',
+ url: '/manualimport',
+ contentType: 'application/json',
+ data: JSON.stringify(requestPayload)
+ });
+
+ abortCurrentRequest = abortRequest;
+ currentIds = payload.ids;
+
+ request.done((data) => {
+ dispatch(batchActions(
+ data.map((item) => updateItem({
+ section,
+ ...item,
+ isReprocessing: false,
+ updateOnly: true
+ }))
+ ));
+ });
+
+ request.fail((xhr) => {
+ if (xhr.aborted) {
+ return;
+ }
+
+ dispatch(batchActions(
+ payload.ids.map((id) => updateItem({
+ section,
+ id,
+ isReprocessing: false,
+ updateOnly: true
+ }))
+ ));
+ });
+ },
[FETCH_INTERACTIVE_IMPORT_ALBUMS]: createFetchHandler(albumsSection, '/album'),
diff --git a/src/Lidarr.Api.V1/ManualImport/ManualImportController.cs b/src/Lidarr.Api.V1/ManualImport/ManualImportController.cs
index eab2f1b83..b11c36a91 100644
--- a/src/Lidarr.Api.V1/ManualImport/ManualImportController.cs
+++ b/src/Lidarr.Api.V1/ManualImport/ManualImportController.cs
@@ -32,8 +32,8 @@ namespace Lidarr.Api.V1.ManualImport
_logger = logger;
}
- [HttpPut]
- public IActionResult UpdateItems(List resource)
+ [HttpPost]
+ public IActionResult UpdateItems(List resource)
{
return Accepted(UpdateImportItems(resource));
}
@@ -65,7 +65,7 @@ namespace Lidarr.Api.V1.ManualImport
return item;
}
- private List UpdateImportItems(List resources)
+ private List UpdateImportItems(List resources)
{
var items = new List();
foreach (var resource in resources)
@@ -75,11 +75,11 @@ namespace Lidarr.Api.V1.ManualImport
Id = resource.Id,
Path = resource.Path,
Name = resource.Name,
- Size = resource.Size,
- Artist = resource.Artist == null ? null : _artistService.GetArtist(resource.Artist.Id),
- Album = resource.Album == null ? null : _albumService.GetAlbum(resource.Album.Id),
- Release = resource.AlbumReleaseId == 0 ? null : _releaseService.GetRelease(resource.AlbumReleaseId),
+ Artist = resource.ArtistId.HasValue ? _artistService.GetArtist(resource.ArtistId.Value) : null,
+ Album = resource.AlbumId.HasValue ? _albumService.GetAlbum(resource.AlbumId.Value) : null,
+ Release = resource.AlbumReleaseId.HasValue ? _releaseService.GetRelease(resource.AlbumReleaseId.Value) : null,
Quality = resource.Quality,
+ ReleaseGroup = resource.ReleaseGroup,
DownloadId = resource.DownloadId,
AdditionalFile = resource.AdditionalFile,
ReplaceExistingFiles = resource.ReplaceExistingFiles,
diff --git a/src/Lidarr.Api.V1/ManualImport/ManualImportResource.cs b/src/Lidarr.Api.V1/ManualImport/ManualImportResource.cs
index 960e59262..4b38b4f7c 100644
--- a/src/Lidarr.Api.V1/ManualImport/ManualImportResource.cs
+++ b/src/Lidarr.Api.V1/ManualImport/ManualImportResource.cs
@@ -21,6 +21,7 @@ namespace Lidarr.Api.V1.ManualImport
public int AlbumReleaseId { get; set; }
public List Tracks { get; set; }
public QualityModel Quality { get; set; }
+ public string ReleaseGroup { get; set; }
public int QualityWeight { get; set; }
public string DownloadId { get; set; }
public IEnumerable Rejections { get; set; }
@@ -50,6 +51,7 @@ namespace Lidarr.Api.V1.ManualImport
AlbumReleaseId = model.Release?.Id ?? 0,
Tracks = model.Tracks.ToResource(),
Quality = model.Quality,
+ ReleaseGroup = model.ReleaseGroup,
// QualityWeight
DownloadId = model.DownloadId,
diff --git a/src/Lidarr.Api.V1/ManualImport/ManualImportUpdateResource.cs b/src/Lidarr.Api.V1/ManualImport/ManualImportUpdateResource.cs
new file mode 100644
index 000000000..c544b3500
--- /dev/null
+++ b/src/Lidarr.Api.V1/ManualImport/ManualImportUpdateResource.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+using Lidarr.Api.V1.Albums;
+using Lidarr.Api.V1.Tracks;
+using Lidarr.Http.REST;
+using NzbDrone.Core.DecisionEngine;
+using NzbDrone.Core.Languages;
+using NzbDrone.Core.Qualities;
+
+namespace Lidarr.Api.V1.ManualImport
+{
+ public class ManualImportUpdateResource : RestResource
+ {
+ public string Path { get; set; }
+ public string Name { get; set; }
+ public int? ArtistId { get; set; }
+ public int? AlbumId { get; set; }
+ public int? AlbumReleaseId { get; set; }
+ public List Tracks { get; set; }
+ public List TrackIds { get; set; }
+ public QualityModel Quality { get; set; }
+ public string ReleaseGroup { get; set; }
+ public string DownloadId { get; set; }
+ public bool AdditionalFile { get; set; }
+ public bool ReplaceExistingFiles { get; set; }
+ public bool DisableReleaseSwitching { get; set; }
+
+ public IEnumerable Rejections { get; set; }
+ }
+}
diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json
index a6e3e6577..b03d3b4da 100644
--- a/src/NzbDrone.Core/Localization/Core/en.json
+++ b/src/NzbDrone.Core/Localization/Core/en.json
@@ -119,11 +119,13 @@
"ChmodFolder": "chmod Folder",
"ChmodFolderHelpText": "Octal, applied during import/rename to media folders and files (without execute bits)",
"ChmodFolderHelpTextWarning": "This only works if the user running Lidarr is the owner of the file. It's better to ensure the download client sets the permissions properly.",
+ "ChooseImportMethod": "Choose Import Method",
"ChownGroup": "chown Group",
"ChownGroupHelpText": "Group name or gid. Use gid for remote file systems.",
"ChownGroupHelpTextWarning": "This only works if the user running Lidarr is the owner of the file. It's better to ensure the download client uses the same group as Lidarr.",
"Clear": "Clear",
"ClickToChangeQuality": "Click to change quality",
+ "ClickToChangeReleaseGroup": "Click to change release group",
"ClientPriority": "Client Priority",
"CloneIndexer": "Clone Indexer",
"CloneProfile": "Clone Profile",
@@ -497,11 +499,11 @@
"NotificationTriggers": "Notification Triggers",
"NoUpdatesAreAvailable": "No updates are available",
"Ok": "Ok",
- "OnAlbumDeleteHelpText": "On Album Delete",
"OnAlbumDelete": "On Album Delete",
- "OnArtistDelete": "On Artist Delete",
+ "OnAlbumDeleteHelpText": "On Album Delete",
"OnApplicationUpdate": "On Application Update",
"OnApplicationUpdateHelpText": "On Application Update",
+ "OnArtistDelete": "On Artist Delete",
"OnArtistDeleteHelpText": "On Artist Delete",
"OnDownloadFailure": "On Download Failure",
"OnDownloadFailureHelpText": "On Download Failure",
@@ -692,6 +694,7 @@
"SelectedCountArtistsSelectedInterp": "{0} Artist(s) Selected",
"SelectFolder": "Select Folder",
"SelectQuality": "Select Quality",
+ "SelectReleaseGroup": "Select Release Group",
"SelectTracks": "Select Tracks",
"SendAnonymousUsageData": "Send Anonymous Usage Data",
"SetPermissions": "Set Permissions",
diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportItem.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportItem.cs
index f0878b774..27cd5ef04 100644
--- a/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportItem.cs
+++ b/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportItem.cs
@@ -22,6 +22,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
public AlbumRelease Release { get; set; }
public List