diff --git a/frontend/src/InteractiveImport/Author/SelectAuthorModalContentConnector.js b/frontend/src/InteractiveImport/Author/SelectAuthorModalContentConnector.js
index c9e3c7dcc..2aeaccc79 100644
--- a/frontend/src/InteractiveImport/Author/SelectAuthorModalContentConnector.js
+++ b/frontend/src/InteractiveImport/Author/SelectAuthorModalContentConnector.js
@@ -53,7 +53,7 @@ class SelectAuthorModalContentConnector extends Component {
});
});
- this.props.saveInteractiveImportItem({ id: ids });
+ this.props.saveInteractiveImportItem({ ids });
this.props.onModalClose(true);
};
diff --git a/frontend/src/InteractiveImport/Book/SelectBookModalContentConnector.js b/frontend/src/InteractiveImport/Book/SelectBookModalContentConnector.js
index a666505af..4277e9436 100644
--- a/frontend/src/InteractiveImport/Book/SelectBookModalContentConnector.js
+++ b/frontend/src/InteractiveImport/Book/SelectBookModalContentConnector.js
@@ -69,7 +69,7 @@ class SelectBookModalContentConnector extends Component {
});
});
- this.props.saveInteractiveImportItem({ id: ids });
+ this.props.saveInteractiveImportItem({ ids });
this.props.onModalClose(true);
};
diff --git a/frontend/src/InteractiveImport/Edition/SelectEditionModalContentConnector.js b/frontend/src/InteractiveImport/Edition/SelectEditionModalContentConnector.js
index e5d0e9340..1b348d41c 100644
--- a/frontend/src/InteractiveImport/Edition/SelectEditionModalContentConnector.js
+++ b/frontend/src/InteractiveImport/Edition/SelectEditionModalContentConnector.js
@@ -78,7 +78,7 @@ class SelectEditionModalContentConnector 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 55fbf2b1d..43f77705d 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js
@@ -21,7 +21,9 @@ import SelectBookModal from 'InteractiveImport/Book/SelectBookModal';
import ConfirmImportModal from 'InteractiveImport/Confirmation/ConfirmImportModal';
import SelectEditionModal from 'InteractiveImport/Edition/SelectEditionModal';
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';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
@@ -46,6 +48,11 @@ const columns = [
label: 'Book',
isVisible: true
},
+ {
+ name: 'releaseGroup',
+ label: 'Release Group',
+ isVisible: true
+ },
{
name: 'quality',
label: 'Quality',
@@ -75,14 +82,16 @@ const filterExistingFilesOptions = {
};
const importModeOptions = [
- { key: 'move', value: 'Move Files' },
- { key: 'copy', value: 'Hardlink/Copy Files' }
+ { key: 'chooseImportMode', value: translate('ChooseImportMethod'), disabled: true },
+ { key: 'move', value: translate('MoveFiles') },
+ { key: 'copy', value: translate('HardlinkCopyFiles') }
];
const SELECT = 'select';
const AUTHOR = 'author';
const BOOK = 'book';
const EDITION = 'edition';
+const RELEASE_GROUP = 'releaseGroup';
const QUALITY = 'quality';
const replaceExistingFilesOptions = {
@@ -280,7 +289,8 @@ class InteractiveImportModalContent extends Component {
{ key: SELECT, value: 'Select...', disabled: true },
{ key: BOOK, value: 'Select Book' },
{ key: EDITION, value: 'Select Edition' },
- { key: QUALITY, value: 'Select Quality' }
+ { key: QUALITY, value: 'Select Quality' },
+ { key: RELEASE_GROUP, value: 'Select ReleaseGroup' }
];
if (allowAuthorChange) {
@@ -483,6 +493,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 742fb904e..76e7182bc 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css
@@ -17,10 +17,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 3f0bbbcd0..674402609 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js
@@ -3,7 +3,6 @@ import React, { Component } from 'react';
import BookQuality from 'Book/BookQuality';
import FileDetails from 'BookFile/FileDetails';
import Icon from 'Components/Icon';
-import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRowCellButton from 'Components/Table/Cells/TableRowCellButton';
@@ -15,6 +14,7 @@ import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
import SelectAuthorModal from 'InteractiveImport/Author/SelectAuthorModal';
import SelectBookModal from 'InteractiveImport/Book/SelectBookModal';
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
+import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import InteractiveImportRowCellPlaceholder from './InteractiveImportRowCellPlaceholder';
@@ -32,6 +32,7 @@ class InteractiveImportRow extends Component {
isDetailsModalOpen: false,
isSelectAuthorModalOpen: false,
isSelectBookModalOpen: false,
+ isSelectReleaseGroupModalOpen: false,
isSelectQualityModalOpen: false
};
}
@@ -123,6 +124,10 @@ class InteractiveImportRow extends Component {
this.setState({ isSelectBookModalOpen: true });
};
+ onSelectReleaseGroupPress = () => {
+ this.setState({ isSelectReleaseGroupModalOpen: true });
+ };
+
onSelectQualityPress = () => {
this.setState({ isSelectQualityModalOpen: true });
};
@@ -137,6 +142,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);
@@ -153,19 +163,21 @@ class InteractiveImportRow extends Component {
author,
book,
quality,
+ releaseGroup,
size,
rejections,
additionalFile,
isSelected,
+ isReprocessing,
onSelectedChange,
- audioTags,
- isSaving
+ audioTags
} = this.props;
const {
isDetailsModalOpen,
isSelectAuthorModalOpen,
isSelectBookModalOpen,
+ isSelectReleaseGroupModalOpen,
isSelectQualityModalOpen
} = this.state;
@@ -176,7 +188,8 @@ class InteractiveImportRow extends Component {
}
const showAuthorPlaceholder = isSelected && !author;
- const showBookNumberPlaceholder = isSelected && !!author && !book;
+ const showBookNumberPlaceholder = !isReprocessing && isSelected && !!author && !book;
+ const showReleaseGroupPlaceholder = isSelected && !releaseGroup;
const showQualityPlaceholder = isSelected && !quality;
const pathCellContents = (
@@ -237,6 +250,17 @@ class InteractiveImportRow extends Component {
}
+
+ {
+ showReleaseGroupPlaceholder ?
+ :
+ releaseGroup
+ }
+
+
{
- isSaving &&
-
- }
- {
- !isSaving && rejections && rejections.length ?
+ rejections && rejections.length ?
}
position={tooltipPositions.LEFT}
+ canFlip={false}
/> :
null
}
@@ -322,6 +340,13 @@ class InteractiveImportRow extends Component {
onModalClose={this.onSelectBookModalClose}
/>
+
+
@@ -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 451f2a560..129d4e167 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 booksSection = `${section}.books`;
const bookFilesSection = `${section}.bookFiles`;
+let abortCurrentRequest = null;
+let currentIds = [];
const MAXIMUM_RECENT_FOLDERS = 10;
@@ -34,7 +35,7 @@ export const defaultState = {
sortKey: 'quality',
sortDirection: sortDirections.DESCENDING,
recentFolders: [],
- importMode: 'move',
+ importMode: 'chooseImportMode',
sortPredicates: {
path: function(item, direction) {
const path = item.path;
@@ -156,7 +157,82 @@ 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,
+ authorId: item.author ? item.author.id : undefined,
+ bookId: item.book ? item.book.id : undefined,
+ foreignEditionId: item.foreignEditionId ? item.ForeignEditionId : undefined,
+ 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_BOOKS]: createFetchHandler(booksSection, '/book'),
diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json
index bd2e82d9e..db05b2a86 100644
--- a/src/NzbDrone.Core/Localization/Core/en.json
+++ b/src/NzbDrone.Core/Localization/Core/en.json
@@ -110,11 +110,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 Readarr 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 Readarr is the owner of the file. It's better to ensure the download client uses the same group as Readarr.",
"Clear": "Clear",
"ClickToChangeQuality": "Click to change quality",
+ "ClickToChangeReleaseGroup": "Click to change release group",
"ClientPriority": "Client Priority",
"CloneIndexer": "Clone Indexer",
"CloneProfile": "Clone Profile",
@@ -298,6 +300,7 @@
"GrabReleaseMessageText": "Readarr was unable to determine which author and book this release was for. Readarr may be unable to automatically import this release. Do you want to grab '{0}'?",
"GrabSelected": "Grab Selected",
"Group": "Group",
+ "HardlinkCopyFiles": "Hardlink/Copy Files",
"HasMonitoredBooksNoMonitoredBooksForThisAuthor": "No monitored books for this author",
"HasPendingChangesNoChanges": "No Changes",
"HasPendingChangesSaveChanges": "Save Changes",
@@ -457,6 +460,7 @@
"MonoVersion": "Mono Version",
"MoreInfo": "More Info",
"MountCheckMessage": "Mount containing an author path is mounted read-only: ",
+ "MoveFiles": "Move Files",
"MusicBrainzAuthorID": "MusicBrainz Author ID",
"MusicBrainzBookID": "MusicBrainz Book ID",
"MusicbrainzId": "Musicbrainz Id",
@@ -487,6 +491,8 @@
"NotificationTriggers": "Notification Triggers",
"NotMonitored": "Not Monitored",
"NoUpdatesAreAvailable": "No updates are available",
+ "OnApplicationUpdate": "On Application Update",
+ "OnApplicationUpdateHelpText": "On Application Update",
"OnAuthorDelete": "On Author Delete",
"OnAuthorDeleteHelpText": "On Author Delete",
"OnBookDelete": "On Book Delete",
@@ -497,8 +503,6 @@
"OnBookFileDeleteHelpText": "On Book File Delete",
"OnBookRetagHelpText": "On Book Retag",
"OnBookTagUpdate": "On Book Tag Update",
- "OnApplicationUpdate": "On Application Update",
- "OnApplicationUpdateHelpText": "On Application Update",
"OnDownloadFailure": "On Download Failure",
"OnDownloadFailureHelpText": "On Download Failure",
"OnGrab": "On Grab",
diff --git a/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportItem.cs b/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportItem.cs
index 9184ae2a8..0421ca95c 100644
--- a/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportItem.cs
+++ b/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportItem.cs
@@ -20,6 +20,7 @@ namespace NzbDrone.Core.MediaFiles.BookImport.Manual
public Book Book { get; set; }
public Edition Edition { get; set; }
public QualityModel Quality { get; set; }
+ public string ReleaseGroup { get; set; }
public string DownloadId { get; set; }
public IEnumerable Rejections { get; set; }
public ParsedTrackInfo Tags { get; set; }
diff --git a/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportService.cs
index 71a8f6173..5f7346112 100644
--- a/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportService.cs
+++ b/src/NzbDrone.Core/MediaFiles/BookImport/Manual/ManualImportService.cs
@@ -19,6 +19,7 @@ using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
+using NzbDrone.Core.Qualities;
using NzbDrone.Core.RootFolders;
namespace NzbDrone.Core.MediaFiles.BookImport.Manual
@@ -236,7 +237,18 @@ namespace NzbDrone.Core.MediaFiles.BookImport.Manual
item.Edition = decision.Item.Edition;
}
+ if (item.Quality?.Quality == Quality.Unknown)
+ {
+ item.Quality = decision.Item.Quality;
+ }
+
+ if (item.ReleaseGroup.IsNullOrWhiteSpace())
+ {
+ item.ReleaseGroup = decision.Item.ReleaseGroup;
+ }
+
item.Rejections = decision.Rejections;
+ item.Size = decision.Item.Size;
result.Add(item);
}
diff --git a/src/Readarr.Api.V1/ManualImport/ManualImportController.cs b/src/Readarr.Api.V1/ManualImport/ManualImportController.cs
index 053beb0c8..d36e5d2ff 100644
--- a/src/Readarr.Api.V1/ManualImport/ManualImportController.cs
+++ b/src/Readarr.Api.V1/ManualImport/ManualImportController.cs
@@ -32,8 +32,8 @@ namespace Readarr.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 Readarr.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 Readarr.Api.V1.ManualImport
Id = resource.Id,
Path = resource.Path,
Name = resource.Name,
- Size = resource.Size,
- Author = resource.Author == null ? null : _authorService.GetAuthor(resource.Author.Id),
- Book = resource.Book == null ? null : _bookService.GetBook(resource.Book.Id),
+ Author = resource.AuthorId.HasValue ? _authorService.GetAuthor(resource.AuthorId.Value) : null,
+ Book = resource.BookId.HasValue ? _bookService.GetBook(resource.BookId.Value) : null,
Edition = resource.ForeignEditionId == null ? null : _editionService.GetEditionByForeignEditionId(resource.ForeignEditionId),
Quality = resource.Quality,
+ ReleaseGroup = resource.ReleaseGroup,
DownloadId = resource.DownloadId,
AdditionalFile = resource.AdditionalFile,
ReplaceExistingFiles = resource.ReplaceExistingFiles,
diff --git a/src/Readarr.Api.V1/ManualImport/ManualImportResource.cs b/src/Readarr.Api.V1/ManualImport/ManualImportResource.cs
index f7341df30..763d6959a 100644
--- a/src/Readarr.Api.V1/ManualImport/ManualImportResource.cs
+++ b/src/Readarr.Api.V1/ManualImport/ManualImportResource.cs
@@ -19,6 +19,7 @@ namespace Readarr.Api.V1.ManualImport
public BookResource Book { get; set; }
public string ForeignEditionId { 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; }
@@ -47,6 +48,7 @@ namespace Readarr.Api.V1.ManualImport
Book = model.Book.ToResource(),
ForeignEditionId = model.Edition?.ForeignEditionId ?? model.Book?.Editions.Value.Single(x => x.Monitored).ForeignEditionId,
Quality = model.Quality,
+ ReleaseGroup = model.ReleaseGroup,
//QualityWeight
DownloadId = model.DownloadId,
diff --git a/src/Readarr.Api.V1/ManualImport/ManualImportUpdateResource.cs b/src/Readarr.Api.V1/ManualImport/ManualImportUpdateResource.cs
new file mode 100644
index 000000000..b87f12489
--- /dev/null
+++ b/src/Readarr.Api.V1/ManualImport/ManualImportUpdateResource.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using NzbDrone.Core.DecisionEngine;
+using NzbDrone.Core.Qualities;
+using Readarr.Http.REST;
+
+namespace Readarr.Api.V1.ManualImport
+{
+ public class ManualImportUpdateResource : RestResource
+ {
+ public string Path { get; set; }
+ public string Name { get; set; }
+ public int? AuthorId { get; set; }
+ public int? BookId { get; set; }
+ public string ForeignEditionId { 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; }
+ }
+}