mirror of
https://github.com/Readarr/Readarr
synced 2025-12-10 10:22:31 +01:00
New: Option to control which new author books get monitored
This commit is contained in:
parent
1d694af98e
commit
c51ae664aa
42 changed files with 774 additions and 265 deletions
|
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function AuthorMonitorNewItemsOptionsPopoverContent() {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('AllBooks')}
|
||||
data="Monitor all new books"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('NewBooks')}
|
||||
data="Monitor new books released after the newest existing book"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('None')}
|
||||
data="Don't monitor any new books"
|
||||
/>
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthorMonitorNewItemsOptionsPopoverContent;
|
||||
|
|
@ -1,46 +1,52 @@
|
|||
import React from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function AuthorMonitoringOptionsPopoverContent() {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('AllBooks')}
|
||||
data="Monitor all books"
|
||||
/>
|
||||
<>
|
||||
<Alert>
|
||||
This is a one time adjustment to set which books are monitored
|
||||
</Alert>
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('AllBooks')}
|
||||
data="Monitor all books"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('FutureBooks')}
|
||||
data="Monitor books that have not released yet"
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('FutureBooks')}
|
||||
data="Monitor books that have not released yet"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('MissingBooks')}
|
||||
data="Monitor books that do not have files or have not released yet"
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('MissingBooks')}
|
||||
data="Monitor books that do not have files or have not released yet"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('ExistingBooks')}
|
||||
data="Monitor books that have files or have not released yet"
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('ExistingBooks')}
|
||||
data="Monitor books that have files or have not released yet"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('FirstBook')}
|
||||
data="Monitor the first book. All other books will be ignored"
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('FirstBook')}
|
||||
data="Monitor the first book. All other books will be ignored"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('LatestBook')}
|
||||
data="Monitor the latest book and future books"
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('LatestBook')}
|
||||
data="Monitor the latest book and future books"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('None')}
|
||||
data="No books will be monitored"
|
||||
/>
|
||||
</DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('None')}
|
||||
data="No books will be monitored"
|
||||
/>
|
||||
</DescriptionList>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import AuthorMetadataProfilePopoverContent from 'AddAuthor/AuthorMetadataProfilePopoverContent';
|
||||
import AuthorMonitorNewItemsOptionsPopoverContent from 'AddAuthor/AuthorMonitorNewItemsOptionsPopoverContent';
|
||||
import MoveAuthorModal from 'Author/MoveAuthor/MoveAuthorModal';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
|
|
@ -73,6 +74,7 @@ class EditAuthorModalContent extends Component {
|
|||
|
||||
const {
|
||||
monitored,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
path,
|
||||
|
|
@ -101,6 +103,31 @@ class EditAuthorModalContent extends Component {
|
|||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('MonitorNewItems')}
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitorNewItems')}
|
||||
body={<AuthorMonitorNewItemsOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.MONITOR_NEW_ITEMS_SELECT}
|
||||
name="monitorNewItems"
|
||||
helpText={translate('MonitorNewItemsHelpText')}
|
||||
{...monitorNewItems}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('QualityProfile')}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ function createMapStateToProps() {
|
|||
|
||||
const authorSettings = _.pick(author, [
|
||||
'monitored',
|
||||
'monitorNewItems',
|
||||
'qualityProfileId',
|
||||
'metadataProfileId',
|
||||
'path',
|
||||
|
|
|
|||
|
|
@ -1,11 +1,23 @@
|
|||
.footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.dropdownContainer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.inputContainer {
|
||||
flex: 1;
|
||||
margin-right: 20px;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
|
|
@ -24,12 +36,14 @@
|
|||
composes: button from '~Components/Link/SpinnerButton.css';
|
||||
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.deleteSelectedButton {
|
||||
composes: button from '~Components/Link/SpinnerButton.css';
|
||||
|
||||
margin-bottom: 10px;
|
||||
margin-left: 50px;
|
||||
height: 35px;
|
||||
}
|
||||
|
|
@ -48,6 +62,10 @@
|
|||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointSmall) {
|
||||
.dropdownContainer {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.inputContainer {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
|
@ -61,6 +79,7 @@
|
|||
}
|
||||
|
||||
.buttons {
|
||||
display: block;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import MoveAuthorModal from 'Author/MoveAuthor/MoveAuthorModal';
|
||||
import MetadataProfileSelectInputConnector from 'Components/Form/MetadataProfileSelectInputConnector';
|
||||
import MonitorNewItemsSelectInput from 'Components/Form/MonitorNewItemsSelectInput';
|
||||
import QualityProfileSelectInputConnector from 'Components/Form/QualityProfileSelectInputConnector';
|
||||
import RootFolderSelectInputConnector from 'Components/Form/RootFolderSelectInputConnector';
|
||||
import SelectInput from 'Components/Form/SelectInput';
|
||||
|
|
@ -26,6 +27,7 @@ class AuthorEditorFooter extends Component {
|
|||
|
||||
this.state = {
|
||||
monitored: NO_CHANGE,
|
||||
monitorNewItems: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
metadataProfileId: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
|
|
@ -46,6 +48,7 @@ class AuthorEditorFooter extends Component {
|
|||
if (prevProps.isSaving && !isSaving && !saveError) {
|
||||
this.setState({
|
||||
monitored: NO_CHANGE,
|
||||
monitorNewItems: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
metadataProfileId: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
|
|
@ -145,6 +148,7 @@ class AuthorEditorFooter extends Component {
|
|||
|
||||
const {
|
||||
monitored,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
rootFolderPath,
|
||||
|
|
@ -163,83 +167,99 @@ class AuthorEditorFooter extends Component {
|
|||
|
||||
return (
|
||||
<PageContentFooter>
|
||||
<div className={styles.inputContainer}>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('MonitorAuthor')}
|
||||
isSaving={isSaving && monitored !== NO_CHANGE}
|
||||
/>
|
||||
<div className={styles.footer}>
|
||||
<div className={styles.dropdownContainer}>
|
||||
<div className={styles.inputContainer}>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('MonitorAuthor')}
|
||||
isSaving={isSaving && monitored !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
name="monitored"
|
||||
value={monitored}
|
||||
values={monitoredOptions}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
<SelectInput
|
||||
name="monitored"
|
||||
value={monitored}
|
||||
values={monitoredOptions}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={styles.inputContainer}
|
||||
>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('QualityProfile')}
|
||||
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
|
||||
/>
|
||||
<div className={styles.inputContainer}>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('MonitorNewItems')}
|
||||
isSaving={isSaving && monitored !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<QualityProfileSelectInputConnector
|
||||
name="qualityProfileId"
|
||||
value={qualityProfileId}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
<MonitorNewItemsSelectInput
|
||||
name="monitorNewItems"
|
||||
value={monitorNewItems}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={styles.inputContainer}
|
||||
>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('MetadataProfile')}
|
||||
isSaving={isSaving && metadataProfileId !== NO_CHANGE}
|
||||
/>
|
||||
<div className={styles.inputContainer}>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('QualityProfile')}
|
||||
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<MetadataProfileSelectInputConnector
|
||||
name="metadataProfileId"
|
||||
value={metadataProfileId}
|
||||
includeNoChange={true}
|
||||
includeNone={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
<QualityProfileSelectInputConnector
|
||||
name="qualityProfileId"
|
||||
value={qualityProfileId}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={styles.inputContainer}
|
||||
>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('RootFolder')}
|
||||
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
|
||||
/>
|
||||
<div
|
||||
className={styles.inputContainer}
|
||||
>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('MetadataProfile')}
|
||||
isSaving={isSaving && metadataProfileId !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<RootFolderSelectInputConnector
|
||||
name="rootFolderPath"
|
||||
value={rootFolderPath}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
selectedValueOptions={{ includeFreeSpace: false }}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
<MetadataProfileSelectInputConnector
|
||||
name="metadataProfileId"
|
||||
value={metadataProfileId}
|
||||
includeNoChange={true}
|
||||
includeNone={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.buttonContainer}>
|
||||
<div className={styles.buttonContainerContent}>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('SelectedCountAuthorsSelectedInterp', [selectedCount])}
|
||||
isSaving={false}
|
||||
/>
|
||||
<div
|
||||
className={styles.inputContainer}
|
||||
>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('RootFolder')}
|
||||
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<RootFolderSelectInputConnector
|
||||
name="rootFolderPath"
|
||||
value={rootFolderPath}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
selectedValueOptions={{ includeFreeSpace: false }}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.buttonContainer}>
|
||||
<div className={styles.buttonContainerContent}>
|
||||
<AuthorEditorFooterLabel
|
||||
label={translate('SelectedCountAuthorsSelectedInterp', [selectedCount])}
|
||||
isSaving={false}
|
||||
/>
|
||||
|
||||
<div className={styles.buttons}>
|
||||
|
||||
<div className={styles.buttons}>
|
||||
<div>
|
||||
<SpinnerButton
|
||||
className={styles.organizeSelectedButton}
|
||||
kind={kinds.WARNING}
|
||||
|
|
@ -268,17 +288,18 @@ class AuthorEditorFooter extends Component {
|
|||
>
|
||||
Set Readarr Tags
|
||||
</SpinnerButton>
|
||||
</div>
|
||||
|
||||
<SpinnerButton
|
||||
className={styles.deleteSelectedButton}
|
||||
kind={kinds.DANGER}
|
||||
isSpinning={isDeleting}
|
||||
isDisabled={!selectedCount || isDeleting}
|
||||
onPress={this.onDeleteSelectedPress}
|
||||
>
|
||||
Delete
|
||||
</SpinnerButton>
|
||||
<SpinnerButton
|
||||
className={styles.deleteSelectedButton}
|
||||
kind={kinds.DANGER}
|
||||
isSpinning={isDeleting}
|
||||
isDisabled={!selectedCount || isDeleting}
|
||||
onPress={this.onDeleteSelectedPress}
|
||||
>
|
||||
Delete
|
||||
</SpinnerButton>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
|
|
@ -10,7 +11,7 @@ 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 } from 'Helpers/Props';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
const NO_CHANGE = 'noChange';
|
||||
|
|
@ -92,6 +93,12 @@ class MonitoringOptionsModalContent extends Component {
|
|||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>
|
||||
{translate('MonitorBookExistingOnlyWarning')}
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
<Form {...otherProps}>
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('Monitoring')}</FormLabel>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import MonitorBooksSelectInput from 'Components/Form/MonitorBooksSelectInput';
|
||||
import MonitorNewItemsSelectInput from 'Components/Form/MonitorNewItemsSelectInput';
|
||||
import SelectInput from 'Components/Form/SelectInput';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
||||
|
|
@ -19,7 +20,8 @@ class BookshelfFooter extends Component {
|
|||
|
||||
this.state = {
|
||||
monitored: NO_CHANGE,
|
||||
monitor: NO_CHANGE
|
||||
monitor: NO_CHANGE,
|
||||
monitorNewItems: NO_CHANGE
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +34,8 @@ class BookshelfFooter extends Component {
|
|||
if (prevProps.isSaving && !isSaving && !saveError) {
|
||||
this.setState({
|
||||
monitored: NO_CHANGE,
|
||||
monitor: NO_CHANGE
|
||||
monitor: NO_CHANGE,
|
||||
monitorNewItems: NO_CHANGE
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -47,7 +50,8 @@ class BookshelfFooter extends Component {
|
|||
onUpdateSelectedPress = () => {
|
||||
const {
|
||||
monitor,
|
||||
monitored
|
||||
monitored,
|
||||
monitorNewItems
|
||||
} = this.state;
|
||||
|
||||
const changes = {};
|
||||
|
|
@ -60,6 +64,10 @@ class BookshelfFooter extends Component {
|
|||
changes.monitor = monitor;
|
||||
}
|
||||
|
||||
if (monitorNewItems !== NO_CHANGE) {
|
||||
changes.monitorNewItems = monitorNewItems;
|
||||
}
|
||||
|
||||
this.props.onUpdateSelectedPress(changes);
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +82,8 @@ class BookshelfFooter extends Component {
|
|||
|
||||
const {
|
||||
monitored,
|
||||
monitor
|
||||
monitor,
|
||||
monitorNewItems
|
||||
} = this.state;
|
||||
|
||||
const monitoredOptions = [
|
||||
|
|
@ -83,7 +92,9 @@ class BookshelfFooter extends Component {
|
|||
{ key: 'unmonitored', value: 'Unmonitored' }
|
||||
];
|
||||
|
||||
const noChanges = monitored === NO_CHANGE && monitor === NO_CHANGE;
|
||||
const noChanges = monitored === NO_CHANGE &&
|
||||
monitor === NO_CHANGE &&
|
||||
monitorNewItems === NO_CHANGE;
|
||||
|
||||
return (
|
||||
<PageContentFooter>
|
||||
|
|
@ -103,7 +114,7 @@ class BookshelfFooter extends Component {
|
|||
|
||||
<div className={styles.inputContainer}>
|
||||
<div className={styles.label}>
|
||||
Monitor Books
|
||||
Monitor Existing Books
|
||||
</div>
|
||||
|
||||
<MonitorBooksSelectInput
|
||||
|
|
@ -115,6 +126,20 @@ class BookshelfFooter extends Component {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<div className={styles.label}>
|
||||
Monitor New Books
|
||||
</div>
|
||||
|
||||
<MonitorNewItemsSelectInput
|
||||
name="monitorNewItems"
|
||||
value={monitorNewItems}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className={styles.label}>
|
||||
{selectedCount} Author(s) Selected
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import IndexerSelectInputConnector from './IndexerSelectInputConnector';
|
|||
import KeyValueListInput from './KeyValueListInput';
|
||||
import MetadataProfileSelectInputConnector from './MetadataProfileSelectInputConnector';
|
||||
import MonitorBooksSelectInput from './MonitorBooksSelectInput';
|
||||
import MonitorNewItemsSelectInput from './MonitorNewItemsSelectInput';
|
||||
import NumberInput from './NumberInput';
|
||||
import OAuthInputConnector from './OAuthInputConnector';
|
||||
import PasswordInput from './PasswordInput';
|
||||
|
|
@ -51,6 +52,9 @@ function getComponent(type) {
|
|||
case inputTypes.MONITOR_BOOKS_SELECT:
|
||||
return MonitorBooksSelectInput;
|
||||
|
||||
case inputTypes.MONITOR_NEW_ITEMS_SELECT:
|
||||
return MonitorNewItemsSelectInput;
|
||||
|
||||
case inputTypes.NUMBER:
|
||||
return NumberInput;
|
||||
|
||||
|
|
|
|||
50
frontend/src/Components/Form/MonitorNewItemsSelectInput.js
Normal file
50
frontend/src/Components/Form/MonitorNewItemsSelectInput.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import monitorNewItemsOptions from 'Utilities/Author/monitorNewItemsOptions';
|
||||
import SelectInput from './SelectInput';
|
||||
|
||||
function MonitorNewItemsSelectInput(props) {
|
||||
const {
|
||||
includeNoChange,
|
||||
includeMixed,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const values = [...monitorNewItemsOptions];
|
||||
|
||||
if (includeNoChange) {
|
||||
values.unshift({
|
||||
key: 'noChange',
|
||||
value: 'No Change',
|
||||
disabled: true
|
||||
});
|
||||
}
|
||||
|
||||
if (includeMixed) {
|
||||
values.unshift({
|
||||
key: 'mixed',
|
||||
value: '(Mixed)',
|
||||
disabled: true
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectInput
|
||||
values={values}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
MonitorNewItemsSelectInput.propTypes = {
|
||||
includeNoChange: PropTypes.bool.isRequired,
|
||||
includeMixed: PropTypes.bool.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
MonitorNewItemsSelectInput.defaultProps = {
|
||||
includeNoChange: false,
|
||||
includeMixed: false
|
||||
};
|
||||
|
||||
export default MonitorNewItemsSelectInput;
|
||||
|
|
@ -5,6 +5,7 @@ export const DEVICE = 'device';
|
|||
export const BOOKSHELF = 'bookshelf';
|
||||
export const KEY_VALUE_LIST = 'keyValueList';
|
||||
export const MONITOR_BOOKS_SELECT = 'monitorBooksSelect';
|
||||
export const MONITOR_NEW_ITEMS_SELECT = 'monitorNewItemsSelect';
|
||||
export const NUMBER = 'number';
|
||||
export const OAUTH = 'oauth';
|
||||
export const PASSWORD = 'password';
|
||||
|
|
@ -29,6 +30,7 @@ export const all = [
|
|||
BOOKSHELF,
|
||||
KEY_VALUE_LIST,
|
||||
MONITOR_BOOKS_SELECT,
|
||||
MONITOR_NEW_ITEMS_SELECT,
|
||||
NUMBER,
|
||||
OAUTH,
|
||||
PASSWORD,
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class AddNewAuthorModalContentConnector extends Component {
|
|||
foreignAuthorId,
|
||||
rootFolderPath,
|
||||
monitor,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
tags
|
||||
|
|
@ -66,6 +67,7 @@ class AddNewAuthorModalContentConnector extends Component {
|
|||
foreignAuthorId,
|
||||
rootFolderPath: rootFolderPath.value,
|
||||
monitor: monitor.value,
|
||||
monitorNewItems: monitorNewItems.value,
|
||||
qualityProfileId: qualityProfileId.value,
|
||||
metadataProfileId: metadataProfileId.value,
|
||||
tags: tags.value,
|
||||
|
|
@ -91,6 +93,7 @@ AddNewAuthorModalContentConnector.propTypes = {
|
|||
foreignAuthorId: PropTypes.string.isRequired,
|
||||
rootFolderPath: PropTypes.object,
|
||||
monitor: PropTypes.object.isRequired,
|
||||
monitorNewItems: PropTypes.object.isRequired,
|
||||
qualityProfileId: PropTypes.object,
|
||||
metadataProfileId: PropTypes.object,
|
||||
tags: PropTypes.object.isRequired,
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ class AddNewBookModalContentConnector extends Component {
|
|||
foreignBookId,
|
||||
rootFolderPath,
|
||||
monitor,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
tags
|
||||
|
|
@ -67,6 +68,7 @@ class AddNewBookModalContentConnector extends Component {
|
|||
foreignBookId,
|
||||
rootFolderPath: rootFolderPath.value,
|
||||
monitor: monitor.value,
|
||||
monitorNewItems: monitorNewItems.value,
|
||||
qualityProfileId: qualityProfileId.value,
|
||||
metadataProfileId: metadataProfileId.value,
|
||||
tags: tags.value,
|
||||
|
|
@ -93,6 +95,7 @@ AddNewBookModalContentConnector.propTypes = {
|
|||
foreignBookId: PropTypes.string.isRequired,
|
||||
rootFolderPath: PropTypes.object,
|
||||
monitor: PropTypes.object.isRequired,
|
||||
monitorNewItems: PropTypes.object.isRequired,
|
||||
qualityProfileId: PropTypes.object,
|
||||
metadataProfileId: PropTypes.object,
|
||||
tags: PropTypes.object.isRequired,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import AuthorMetadataProfilePopoverContent from 'AddAuthor/AuthorMetadataProfilePopoverContent';
|
||||
import AuthorMonitoringOptionsPopoverContent from 'AddAuthor/AuthorMonitoringOptionsPopoverContent';
|
||||
import AuthorMonitorNewItemsOptionsPopoverContent from 'AddAuthor/AuthorMonitorNewItemsOptionsPopoverContent';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
|
|
@ -32,6 +33,7 @@ class AddAuthorOptionsForm extends Component {
|
|||
const {
|
||||
rootFolderPath,
|
||||
monitor,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
includeNoneMetadataProfile,
|
||||
|
|
@ -77,12 +79,38 @@ class AddAuthorOptionsForm extends Component {
|
|||
<FormInputGroup
|
||||
type={inputTypes.MONITOR_BOOKS_SELECT}
|
||||
name="monitor"
|
||||
helpText={translate('MonitoringOptionsHelpText')}
|
||||
onChange={onInputChange}
|
||||
includeSpecificBook={includeSpecificBookMonitor}
|
||||
{...monitor}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('MonitorNewItems')}
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitorNewItems')}
|
||||
body={<AuthorMonitorNewItemsOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.MONITOR_NEW_ITEMS_SELECT}
|
||||
name="monitorNewItems"
|
||||
helpText={translate('MonitorNewItemsHelpText')}
|
||||
{...monitorNewItems}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('QualityProfile')}
|
||||
|
|
@ -145,6 +173,7 @@ class AddAuthorOptionsForm extends Component {
|
|||
AddAuthorOptionsForm.propTypes = {
|
||||
rootFolderPath: PropTypes.object,
|
||||
monitor: PropTypes.object.isRequired,
|
||||
monitorNewItems: PropTypes.string.isRequired,
|
||||
qualityProfileId: PropTypes.object,
|
||||
metadataProfileId: PropTypes.object,
|
||||
showMetadataProfile: PropTypes.bool.isRequired,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import AuthorMonitorNewItemsOptionsPopoverContent from 'AddAuthor/AuthorMonitorNewItemsOptionsPopoverContent';
|
||||
import Alert from 'Components/Alert';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
|
|
@ -76,6 +78,7 @@ function EditImportListModalContent(props) {
|
|||
shouldMonitorExisting,
|
||||
shouldSearch,
|
||||
rootFolderPath,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
tags,
|
||||
|
|
@ -114,148 +117,178 @@ function EditImportListModalContent(props) {
|
|||
{message.value.message}
|
||||
</Alert>
|
||||
}
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('Name')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="name"
|
||||
{...name}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FieldSet legend={translate('ImportListSettings')} >
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('Name')}
|
||||
</FormLabel>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('EnableAutomaticAdd')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableAutomaticAdd"
|
||||
helpText={translate('EnableAutomaticAddHelpText')}
|
||||
{...enableAutomaticAdd}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Monitor
|
||||
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitoringOptions')}
|
||||
body={<ImportListMonitoringOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="name"
|
||||
{...name}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormLabel>
|
||||
</FormGroup>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="shouldMonitor"
|
||||
values={monitorOptions}
|
||||
helpText={translate('ShouldMonitorHelpText')}
|
||||
{...shouldMonitor}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('EnableAutomaticAdd')}
|
||||
</FormLabel>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('ShouldMonitorExisting')}
|
||||
</FormLabel>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableAutomaticAdd"
|
||||
helpText={translate('EnableAutomaticAddHelpText')}
|
||||
{...enableAutomaticAdd}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="shouldMonitorExisting"
|
||||
helpText={translate('ShouldMonitorExistingHelpText')}
|
||||
{...shouldMonitorExisting}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Monitor
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('SearchForNewItems')}
|
||||
</FormLabel>
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitoringOptions')}
|
||||
body={<ImportListMonitoringOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="shouldSearch"
|
||||
helpText={translate('ShouldSearchHelpText')}
|
||||
{...shouldSearch}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="shouldMonitor"
|
||||
values={monitorOptions}
|
||||
helpText={translate('ShouldMonitorHelpText')}
|
||||
{...shouldMonitor}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('RootFolder')}
|
||||
</FormLabel>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('ShouldMonitorExisting')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.ROOT_FOLDER_SELECT}
|
||||
name="rootFolderPath"
|
||||
helpText={translate('RootFolderPathHelpText')}
|
||||
{...rootFolderPath}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="shouldMonitorExisting"
|
||||
helpText={translate('ShouldMonitorExistingHelpText')}
|
||||
{...shouldMonitorExisting}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('QualityProfile')}
|
||||
</FormLabel>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('SearchForNewItems')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||
name="qualityProfileId"
|
||||
helpText={translate('QualityProfileIdHelpText')}
|
||||
{...qualityProfileId}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="shouldSearch"
|
||||
helpText={translate('ShouldSearchHelpText')}
|
||||
{...shouldSearch}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet>
|
||||
|
||||
<FormGroup className={showMetadataProfile ? undefined : styles.hideMetadataProfile}>
|
||||
<FormLabel>
|
||||
{translate('MetadataProfile')}
|
||||
</FormLabel>
|
||||
<FieldSet legend={translate('AddedAuthorSettings')} >
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('RootFolder')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.METADATA_PROFILE_SELECT}
|
||||
name="metadataProfileId"
|
||||
helpText={translate('MetadataProfileIdHelpText')}
|
||||
{...metadataProfileId}
|
||||
includeNone={true}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormInputGroup
|
||||
type={inputTypes.ROOT_FOLDER_SELECT}
|
||||
name="rootFolderPath"
|
||||
helpText={translate('RootFolderPathHelpText')}
|
||||
{...rootFolderPath}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('ReadarrTags')}
|
||||
</FormLabel>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('MonitorNewItems')}
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitorNewItems')}
|
||||
body={<AuthorMonitorNewItemsOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
helpText={translate('TagsHelpText')}
|
||||
{...tags}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormInputGroup
|
||||
type={inputTypes.MONITOR_NEW_ITEMS_SELECT}
|
||||
name="monitorNewItems"
|
||||
helpText={translate('MonitorNewItemsHelpText')}
|
||||
{...monitorNewItems}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('QualityProfile')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||
name="qualityProfileId"
|
||||
helpText={translate('QualityProfileIdHelpText')}
|
||||
{...qualityProfileId}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup className={showMetadataProfile ? undefined : styles.hideMetadataProfile}>
|
||||
<FormLabel>
|
||||
{translate('MetadataProfile')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.METADATA_PROFILE_SELECT}
|
||||
name="metadataProfileId"
|
||||
helpText={translate('MetadataProfileIdHelpText')}
|
||||
{...metadataProfileId}
|
||||
includeNone={true}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('ReadarrTags')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
helpText={translate('TagsHelpText')}
|
||||
{...tags}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet>
|
||||
|
||||
{
|
||||
!!fields && !!fields.length &&
|
||||
<div>
|
||||
<FieldSet legend={translate('ImportListSpecificSettings')} >
|
||||
{
|
||||
fields.map((field) => {
|
||||
return (
|
||||
|
|
@ -271,7 +304,7 @@ function EditImportListModalContent(props) {
|
|||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</FieldSet>
|
||||
}
|
||||
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React from 'react';
|
||||
import AuthorMetadataProfilePopoverContent from 'AddAuthor/AuthorMetadataProfilePopoverContent';
|
||||
import AuthorMonitoringOptionsPopoverContent from 'AddAuthor/AuthorMonitoringOptionsPopoverContent';
|
||||
import AuthorMonitorNewItemsOptionsPopoverContent from 'AddAuthor/AuthorMonitorNewItemsOptionsPopoverContent';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
|
|
@ -43,6 +44,7 @@ function EditRootFolderModalContent(props) {
|
|||
defaultQualityProfileId,
|
||||
defaultMetadataProfileId,
|
||||
defaultMonitorOption,
|
||||
defaultNewItemMonitorOption,
|
||||
defaultTags,
|
||||
isCalibreLibrary,
|
||||
host,
|
||||
|
|
@ -295,7 +297,7 @@ function EditRootFolderModalContent(props) {
|
|||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Monitor
|
||||
{translate('Monitor')}
|
||||
|
||||
<Popover
|
||||
anchor={
|
||||
|
|
@ -320,6 +322,31 @@ function EditRootFolderModalContent(props) {
|
|||
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('MonitorNewItems')}
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitorNewItems')}
|
||||
body={<AuthorMonitorNewItemsOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.MONITOR_NEW_ITEMS_SELECT}
|
||||
name="defaultNewItemMonitorOption"
|
||||
{...defaultNewItemMonitorOption}
|
||||
onChange={onInputChange}
|
||||
helpText={translate('MonitorNewItemsHelpText')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('QualityProfile')}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { createThunk, handleThunks } from 'Store/thunks';
|
|||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
||||
import { filterPredicates, filters } from './authorActions';
|
||||
import { set } from './baseActions';
|
||||
import { fetchBooks } from './bookActions';
|
||||
import createHandleActions from './Creators/createHandleActions';
|
||||
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
|
||||
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
|
||||
|
|
@ -97,7 +96,8 @@ export const actionHandlers = handleThunks({
|
|||
const {
|
||||
authorIds,
|
||||
monitored,
|
||||
monitor
|
||||
monitor,
|
||||
monitorNewItems
|
||||
} = payload;
|
||||
|
||||
const authors = [];
|
||||
|
|
@ -122,14 +122,13 @@ export const actionHandlers = handleThunks({
|
|||
method: 'POST',
|
||||
data: JSON.stringify({
|
||||
authors,
|
||||
monitoringOptions: { monitor }
|
||||
monitoringOptions: { monitor },
|
||||
monitorNewItems
|
||||
}),
|
||||
dataType: 'json'
|
||||
}).request;
|
||||
|
||||
promise.done((data) => {
|
||||
dispatch(fetchBooks());
|
||||
|
||||
dispatch(set({
|
||||
section,
|
||||
isSaving: false,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ function getNewAuthor(author, payload) {
|
|||
const {
|
||||
rootFolderPath,
|
||||
monitor,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
tags,
|
||||
|
|
@ -16,6 +17,7 @@ function getNewAuthor(author, payload) {
|
|||
|
||||
author.addOptions = addOptions;
|
||||
author.monitored = true;
|
||||
author.monitorNewItems = monitorNewItems;
|
||||
author.qualityProfileId = qualityProfileId;
|
||||
author.metadataProfileId = metadataProfileId;
|
||||
author.rootFolderPath = rootFolderPath;
|
||||
|
|
|
|||
7
frontend/src/Utilities/Author/monitorNewItemsOptions.js
Normal file
7
frontend/src/Utilities/Author/monitorNewItemsOptions.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
const monitorNewItemsOptions = [
|
||||
{ key: 'all', value: 'All Books' },
|
||||
{ key: 'none', value: 'None' },
|
||||
{ key: 'new', value: 'New' }
|
||||
];
|
||||
|
||||
export default monitorNewItemsOptions;
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.BookTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class MonitorNewBookServiceFixture : CoreTest<MonitorNewBookService>
|
||||
{
|
||||
private List<Book> _books;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_books = Builder<Book>.CreateListOfSize(4)
|
||||
.All()
|
||||
.With(e => e.Monitored = true)
|
||||
.With(e => e.ReleaseDate = DateTime.UtcNow.AddDays(-7))
|
||||
|
||||
//Future
|
||||
.TheFirst(1)
|
||||
.With(e => e.ReleaseDate = DateTime.UtcNow.AddDays(7))
|
||||
|
||||
//Future/TBA
|
||||
.TheNext(1)
|
||||
.With(e => e.ReleaseDate = null)
|
||||
.Build()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_monitor_with_all()
|
||||
{
|
||||
foreach (var book in _books)
|
||||
{
|
||||
Subject.ShouldMonitorNewBook(book, _books, NewItemMonitorTypes.All).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_monitor_with_none()
|
||||
{
|
||||
foreach (var book in _books)
|
||||
{
|
||||
Subject.ShouldMonitorNewBook(book, _books, NewItemMonitorTypes.None).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_monitor_new_with_new()
|
||||
{
|
||||
Subject.ShouldMonitorNewBook(_books[0], _books, NewItemMonitorTypes.New).Should().BeTrue();
|
||||
|
||||
foreach (var book in _books.Skip(1))
|
||||
{
|
||||
Subject.ShouldMonitorNewBook(book, _books, NewItemMonitorTypes.New).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ public void Setup()
|
|||
|
||||
Mocker.GetMock<IMetadataProfileService>()
|
||||
.Setup(s => s.FilterBooks(It.IsAny<Author>(), It.IsAny<int>()))
|
||||
.Returns(_books);
|
||||
.Returns(_remoteBooks);
|
||||
|
||||
Mocker.GetMock<IProvideAuthorInfo>()
|
||||
.Setup(s => s.GetAuthorAndBooks(It.IsAny<string>(), It.IsAny<double>()))
|
||||
|
|
@ -83,6 +83,10 @@ public void Setup()
|
|||
Mocker.GetMock<IRootFolderService>()
|
||||
.Setup(x => x.All())
|
||||
.Returns(new List<RootFolder>());
|
||||
|
||||
Mocker.GetMock<IMonitorNewBookService>()
|
||||
.Setup(x => x.ShouldMonitorNewBook(It.IsAny<Book>(), It.IsAny<List<Book>>(), It.IsAny<NewItemMonitorTypes>()))
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
private void GivenNewAuthorInfo(Author author)
|
||||
|
|
@ -151,6 +155,29 @@ public void should_publish_author_updated_event_if_metadata_updated()
|
|||
VerifyEventPublished<AuthorRefreshCompleteEvent>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_call_new_book_monitor_service_when_adding_book()
|
||||
{
|
||||
var newBook = Builder<Book>.CreateNew()
|
||||
.With(x => x.Id = 0)
|
||||
.With(x => x.ForeignBookId = "3")
|
||||
.Build();
|
||||
_remoteBooks.Add(newBook);
|
||||
|
||||
var newAuthorInfo = _author.JsonClone();
|
||||
newAuthorInfo.Metadata = _author.Metadata.Value.JsonClone();
|
||||
newAuthorInfo.Books = _remoteBooks;
|
||||
|
||||
GivenNewAuthorInfo(newAuthorInfo);
|
||||
GivenBooksForRefresh(_books);
|
||||
AllowAuthorUpdate();
|
||||
|
||||
Subject.Execute(new RefreshAuthorCommand(_author.Id));
|
||||
|
||||
Mocker.GetMock<IMonitorNewBookService>()
|
||||
.Verify(x => x.ShouldMonitorNewBook(newBook, _books, _author.MonitorNewItems), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_log_error_and_delete_if_musicbrainz_id_not_found_and_author_has_no_files()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ public Author()
|
|||
public int AuthorMetadataId { get; set; }
|
||||
public string CleanName { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public NewItemMonitorTypes MonitorNewItems { get; set; }
|
||||
public DateTime? LastInfoSync { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
|
|
@ -70,6 +71,7 @@ public override void UseDbFieldsFrom(Author other)
|
|||
Id = other.Id;
|
||||
AuthorMetadataId = other.AuthorMetadataId;
|
||||
Monitored = other.Monitored;
|
||||
MonitorNewItems = other.MonitorNewItems;
|
||||
LastInfoSync = other.LastInfoSync;
|
||||
Path = other.Path;
|
||||
RootFolderPath = other.RootFolderPath;
|
||||
|
|
@ -93,6 +95,7 @@ public override void ApplyChanges(Author other)
|
|||
AddOptions = other.AddOptions;
|
||||
RootFolderPath = other.RootFolderPath;
|
||||
Monitored = other.Monitored;
|
||||
MonitorNewItems = other.MonitorNewItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
src/NzbDrone.Core/Books/Model/MonitorTypes.cs
Normal file
14
src/NzbDrone.Core/Books/Model/MonitorTypes.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
namespace NzbDrone.Core.Books
|
||||
{
|
||||
public enum MonitorTypes
|
||||
{
|
||||
All,
|
||||
Future,
|
||||
Missing,
|
||||
Existing,
|
||||
Latest,
|
||||
First,
|
||||
None,
|
||||
Unknown
|
||||
}
|
||||
}
|
||||
|
|
@ -14,16 +14,4 @@ public MonitoringOptions()
|
|||
public List<string> BooksToMonitor { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
}
|
||||
|
||||
public enum MonitorTypes
|
||||
{
|
||||
All,
|
||||
Future,
|
||||
Missing,
|
||||
Existing,
|
||||
Latest,
|
||||
First,
|
||||
None,
|
||||
Unknown
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
src/NzbDrone.Core/Books/Model/NewItemMonitorTypes.cs
Normal file
9
src/NzbDrone.Core/Books/Model/NewItemMonitorTypes.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace NzbDrone.Core.Books
|
||||
{
|
||||
public enum NewItemMonitorTypes
|
||||
{
|
||||
All,
|
||||
None,
|
||||
New
|
||||
}
|
||||
}
|
||||
44
src/NzbDrone.Core/Books/Services/MonitorNewBookService.cs
Normal file
44
src/NzbDrone.Core/Books/Services/MonitorNewBookService.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
|
||||
namespace NzbDrone.Core.Books
|
||||
{
|
||||
public interface IMonitorNewBookService
|
||||
{
|
||||
bool ShouldMonitorNewBook(Book addedBook, List<Book> existingBooks, NewItemMonitorTypes author);
|
||||
}
|
||||
|
||||
public class MonitorNewBookService : IMonitorNewBookService
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MonitorNewBookService(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool ShouldMonitorNewBook(Book addedBook, List<Book> existingBooks, NewItemMonitorTypes monitorNewItems)
|
||||
{
|
||||
if (monitorNewItems == NewItemMonitorTypes.None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (monitorNewItems == NewItemMonitorTypes.All)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (monitorNewItems == NewItemMonitorTypes.New)
|
||||
{
|
||||
var newest = existingBooks.OrderByDescending(x => x.ReleaseDate ?? DateTime.MinValue).FirstOrDefault()?.ReleaseDate ?? DateTime.MinValue;
|
||||
|
||||
return (addedBook.ReleaseDate ?? DateTime.MinValue) >= newest;
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"Unknown new item monitor type {monitorNewItems}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,7 @@ public class RefreshAuthorService : RefreshEntityServiceBase<Author, Book>,
|
|||
private readonly IHistoryService _historyService;
|
||||
private readonly IRootFolderService _rootFolderService;
|
||||
private readonly ICheckIfAuthorShouldBeRefreshed _checkIfAuthorShouldBeRefreshed;
|
||||
private readonly IMonitorNewBookService _monitorNewBookService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IImportListExclusionService _importListExclusionService;
|
||||
private readonly Logger _logger;
|
||||
|
|
@ -59,6 +60,7 @@ public RefreshAuthorService(IProvideAuthorInfo authorInfo,
|
|||
IHistoryService historyService,
|
||||
IRootFolderService rootFolderService,
|
||||
ICheckIfAuthorShouldBeRefreshed checkIfAuthorShouldBeRefreshed,
|
||||
IMonitorNewBookService monitorNewBookService,
|
||||
IConfigService configService,
|
||||
IImportListExclusionService importListExclusionService,
|
||||
Logger logger)
|
||||
|
|
@ -76,6 +78,7 @@ public RefreshAuthorService(IProvideAuthorInfo authorInfo,
|
|||
_historyService = historyService;
|
||||
_rootFolderService = rootFolderService;
|
||||
_checkIfAuthorShouldBeRefreshed = checkIfAuthorShouldBeRefreshed;
|
||||
_monitorNewBookService = monitorNewBookService;
|
||||
_configService = configService;
|
||||
_importListExclusionService = importListExclusionService;
|
||||
_logger = logger;
|
||||
|
|
@ -264,6 +267,14 @@ protected override void PrepareExistingChild(Book local, Book remote, Author ent
|
|||
remote.UseDbFieldsFrom(local);
|
||||
}
|
||||
|
||||
protected override void ProcessChildren(Author entity, SortedChildren children)
|
||||
{
|
||||
foreach (var book in children.Added)
|
||||
{
|
||||
book.Monitored = _monitorNewBookService.ShouldMonitorNewBook(book, children.UpToDate, entity.MonitorNewItems);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AddChildren(List<Book> children)
|
||||
{
|
||||
_bookService.InsertMany(children);
|
||||
|
|
|
|||
|
|
@ -93,6 +93,11 @@ protected virtual UpdateResult MergeEntity(TEntity local, TEntity target, TEntit
|
|||
|
||||
protected abstract void PrepareNewChild(TChild child, TEntity entity);
|
||||
protected abstract void PrepareExistingChild(TChild local, TChild remote, TEntity entity);
|
||||
|
||||
protected virtual void ProcessChildren(TEntity entity, SortedChildren children)
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract void AddChildren(List<TChild> children);
|
||||
protected abstract bool RefreshChildren(SortedChildren localChildren, List<TChild> remoteChildren, Author remoteData, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate);
|
||||
|
||||
|
|
@ -277,6 +282,8 @@ protected bool SortChildren(TEntity entity, List<TChild> remoteChildren, Author
|
|||
sortedChildren.Deleted.Count);
|
||||
}
|
||||
|
||||
ProcessChildren(entity, sortedChildren);
|
||||
|
||||
// Add in the new children (we have checked that foreign IDs don't clash)
|
||||
AddChildren(sortedChildren.Added);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(19)]
|
||||
public class AddNewItemMonitorType : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Authors").AddColumn("MonitorNewItems").AsInt32().WithDefaultValue(0);
|
||||
Alter.Table("RootFolders").AddColumn("DefaultNewItemMonitorOption").AsInt32().WithDefaultValue(0);
|
||||
Alter.Table("ImportLists").AddColumn("MonitorNewItems").AsInt32().WithDefaultValue(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists
|
||||
|
|
@ -8,6 +9,7 @@ public class ImportListDefinition : ProviderDefinition
|
|||
public ImportListMonitorType ShouldMonitor { get; set; }
|
||||
public bool ShouldMonitorExisting { get; set; }
|
||||
public bool ShouldSearch { get; set; }
|
||||
public NewItemMonitorTypes MonitorNewItems { get; set; }
|
||||
public int ProfileId { get; set; }
|
||||
public int MetadataProfileId { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ private void ProcessBookReport(ImportListDefinition importList, ImportListItemIn
|
|||
var toAddAuthor = new Author
|
||||
{
|
||||
Monitored = monitored,
|
||||
MonitorNewItems = importList.MonitorNewItems,
|
||||
RootFolderPath = importList.RootFolderPath,
|
||||
QualityProfileId = importList.ProfileId,
|
||||
MetadataProfileId = importList.MetadataProfileId,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
"About": "About",
|
||||
"Absolute": "Absolute",
|
||||
"Actions": "Actions",
|
||||
"AddedAuthorSettings": "Added Author Settings",
|
||||
"AddImportListExclusionHelpText": "Prevent book from being added to Readarr by Import Lists or Author Refresh",
|
||||
"AddingTag": "Adding tag",
|
||||
"AddListExclusion": "Add List Exclusion",
|
||||
|
|
@ -140,7 +141,7 @@
|
|||
"Dates": "Dates",
|
||||
"DBMigration": "DB Migration",
|
||||
"DefaultMetadataProfileIdHelpText": "Default Metadata Profile for authors detected in this folder",
|
||||
"DefaultMonitorOptionHelpText": "Default Monitoring Options for books by authors detected in this folder",
|
||||
"DefaultMonitorOptionHelpText": "Which books should be monitored on initial add for authors detected in this folder",
|
||||
"DefaultQualityProfileIdHelpText": "Default Quality Profile for authors detected in this folder",
|
||||
"DefaultReadarrTags": "Default Readarr Tags",
|
||||
"DefaultTagsHelpText": "Default Readarr Tags for authors detected in this folder",
|
||||
|
|
@ -291,7 +292,8 @@
|
|||
"Importing": "Importing",
|
||||
"ImportListExclusions": "Import List Exclusions",
|
||||
"ImportLists": "Import Lists",
|
||||
"ImportListSettings": "Import List Settings",
|
||||
"ImportListSettings": "General Import List Settings",
|
||||
"ImportListSpecificSettings": "Import List Specific Settings",
|
||||
"IncludeHealthWarningsHelpText": "Include Health Warnings",
|
||||
"IncludePreferredWhenRenaming": "Include Preferred when Renaming",
|
||||
"IncludeUnknownAuthorItemsHelpText": "Show items without a author in the queue, this could include removed authors, movies or anything else in Readarr's category",
|
||||
|
|
@ -381,12 +383,16 @@
|
|||
"Mode": "Mode",
|
||||
"MonitorAuthor": "Monitor Author",
|
||||
"MonitorBook": "Monitor Book",
|
||||
"MonitorBookExistingOnlyWarning": "This is a one off adjustment of the monitored setting for each book. Use the option under Author/Edit to control what happens for newly added books",
|
||||
"Monitored": "Monitored",
|
||||
"MonitoredAuthorIsMonitored": "Author is monitored",
|
||||
"MonitoredAuthorIsUnmonitored": "Author is unmonitored",
|
||||
"MonitoredHelpText": "Readarr will search for and download book",
|
||||
"Monitoring": "Monitoring",
|
||||
"MonitoringOptions": "Monitoring Options",
|
||||
"MonitoringOptionsHelpText": "Which books should be monitored after the author is added (one-time adjustment)",
|
||||
"MonitorNewItems": "Monitor New Books",
|
||||
"MonitorNewItemsHelpText": "Which new books should be monitored",
|
||||
"MonoVersion": "Mono Version",
|
||||
"MoreInfo": "More Info",
|
||||
"MusicBrainzAuthorID": "MusicBrainz Author ID",
|
||||
|
|
@ -404,6 +410,7 @@
|
|||
"NamingSettings": "Naming Settings",
|
||||
"NETCore": ".NET Core",
|
||||
"New": "New",
|
||||
"NewBooks": "New Books",
|
||||
"NoBackupsAreAvailable": "No backups are available",
|
||||
"NoHistory": "No history.",
|
||||
"NoHistoryBlocklist": "No history blocklist",
|
||||
|
|
|
|||
|
|
@ -328,6 +328,7 @@ private Author EnsureAuthorAdded(List<ImportDecision<LocalBook>> decisions, List
|
|||
author.MetadataProfileId = rootFolder.DefaultMetadataProfileId;
|
||||
author.QualityProfileId = rootFolder.DefaultQualityProfileId;
|
||||
author.Monitored = rootFolder.DefaultMonitorOption != MonitorTypes.None;
|
||||
author.MonitorNewItems = rootFolder.DefaultNewItemMonitorOption;
|
||||
author.Tags = rootFolder.DefaultTags;
|
||||
author.AddOptions = new AddAuthorOptions
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public class RootFolder : ModelBase
|
|||
public int DefaultMetadataProfileId { get; set; }
|
||||
public int DefaultQualityProfileId { get; set; }
|
||||
public MonitorTypes DefaultMonitorOption { get; set; }
|
||||
public NewItemMonitorTypes DefaultNewItemMonitorOption { get; set; }
|
||||
public HashSet<int> DefaultTags { get; set; }
|
||||
public bool IsCalibreLibrary { get; set; }
|
||||
public CalibreSettings CalibreSettings { get; set; }
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
using NzbDrone.Core.Validation.Paths;
|
||||
using NzbDrone.Http.REST.Attributes;
|
||||
using NzbDrone.SignalR;
|
||||
using Readarr.Api.V1.Books;
|
||||
using Readarr.Http;
|
||||
using Readarr.Http.Extensions;
|
||||
using Readarr.Http.REST;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ public IActionResult SaveAll([FromBody] AuthorEditorResource resource)
|
|||
author.Monitored = resource.Monitored.Value;
|
||||
}
|
||||
|
||||
if (resource.MonitorNewItems.HasValue)
|
||||
{
|
||||
author.MonitorNewItems = resource.MonitorNewItems.Value;
|
||||
}
|
||||
|
||||
if (resource.QualityProfileId.HasValue)
|
||||
{
|
||||
author.QualityProfileId = resource.QualityProfileId.Value;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Books;
|
||||
|
||||
namespace Readarr.Api.V1.Author
|
||||
{
|
||||
|
|
@ -6,6 +7,7 @@ public class AuthorEditorResource
|
|||
{
|
||||
public List<int> AuthorIds { get; set; }
|
||||
public bool? Monitored { get; set; }
|
||||
public NewItemMonitorTypes? MonitorNewItems { get; set; }
|
||||
public int? QualityProfileId { get; set; }
|
||||
public int? MetadataProfileId { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using Readarr.Api.V1.Books;
|
||||
using Readarr.Http.REST;
|
||||
|
||||
namespace Readarr.Api.V1.Author
|
||||
|
|
@ -43,6 +42,7 @@ public class AuthorResource : RestResource
|
|||
|
||||
//Editing Only
|
||||
public bool Monitored { get; set; }
|
||||
public NewItemMonitorTypes MonitorNewItems { get; set; }
|
||||
|
||||
public string RootFolderPath { get; set; }
|
||||
public List<string> Genres { get; set; }
|
||||
|
|
@ -91,6 +91,7 @@ public static AuthorResource ToResource(this NzbDrone.Core.Books.Author model)
|
|||
Links = model.Metadata.Value.Links,
|
||||
|
||||
Monitored = model.Monitored,
|
||||
MonitorNewItems = model.MonitorNewItems,
|
||||
|
||||
CleanName = model.CleanName,
|
||||
ForeignAuthorId = model.Metadata.Value.ForeignAuthorId,
|
||||
|
|
@ -141,6 +142,7 @@ public static NzbDrone.Core.Books.Author ToModel(this AuthorResource resource)
|
|||
MetadataProfileId = resource.MetadataProfileId,
|
||||
|
||||
Monitored = resource.Monitored,
|
||||
MonitorNewItems = resource.MonitorNewItems,
|
||||
|
||||
CleanName = resource.CleanName,
|
||||
RootFolderPath = resource.RootFolderPath,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public BookshelfController(IAuthorService authorService, IBookMonitoredService b
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult<object> UpdateAll([FromBody] BookshelfResource request)
|
||||
public IActionResult UpdateAll([FromBody] BookshelfResource request)
|
||||
{
|
||||
//Read from request
|
||||
var authorToUpdate = _authorService.GetAuthors(request.Authors.Select(s => s.Id));
|
||||
|
|
@ -37,10 +37,15 @@ public ActionResult<object> UpdateAll([FromBody] BookshelfResource request)
|
|||
author.Monitored = false;
|
||||
}
|
||||
|
||||
if (request.MonitorNewItems.HasValue)
|
||||
{
|
||||
author.MonitorNewItems = request.MonitorNewItems.Value;
|
||||
}
|
||||
|
||||
_bookMonitoredService.SetBookMonitoredStatus(author, request.MonitoringOptions);
|
||||
}
|
||||
|
||||
return Accepted(new object());
|
||||
return Accepted(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,5 +7,6 @@ public class BookshelfResource
|
|||
{
|
||||
public List<BookshelfAuthorResource> Authors { get; set; }
|
||||
public MonitoringOptions MonitoringOptions { get; set; }
|
||||
public NewItemMonitorTypes? MonitorNewItems { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.ImportLists;
|
||||
|
||||
namespace Readarr.Api.V1.ImportLists
|
||||
|
|
@ -9,6 +10,7 @@ public class ImportListResource : ProviderResource<ImportListResource>
|
|||
public bool ShouldMonitorExisting { get; set; }
|
||||
public bool ShouldSearch { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
public NewItemMonitorTypes MonitorNewItems { get; set; }
|
||||
public int QualityProfileId { get; set; }
|
||||
public int MetadataProfileId { get; set; }
|
||||
public ImportListType ListType { get; set; }
|
||||
|
|
@ -31,6 +33,7 @@ public override ImportListResource ToResource(ImportListDefinition definition)
|
|||
resource.ShouldMonitorExisting = definition.ShouldMonitorExisting;
|
||||
resource.ShouldSearch = definition.ShouldSearch;
|
||||
resource.RootFolderPath = definition.RootFolderPath;
|
||||
resource.MonitorNewItems = definition.MonitorNewItems;
|
||||
resource.QualityProfileId = definition.ProfileId;
|
||||
resource.MetadataProfileId = definition.MetadataProfileId;
|
||||
resource.ListType = definition.ListType;
|
||||
|
|
@ -53,6 +56,7 @@ public override ImportListDefinition ToModel(ImportListResource resource)
|
|||
definition.ShouldMonitorExisting = resource.ShouldMonitorExisting;
|
||||
definition.ShouldSearch = resource.ShouldSearch;
|
||||
definition.RootFolderPath = resource.RootFolderPath;
|
||||
definition.MonitorNewItems = resource.MonitorNewItems;
|
||||
definition.ProfileId = resource.QualityProfileId;
|
||||
definition.MetadataProfileId = resource.MetadataProfileId;
|
||||
definition.ListType = resource.ListType;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public class RootFolderResource : RestResource
|
|||
public int DefaultMetadataProfileId { get; set; }
|
||||
public int DefaultQualityProfileId { get; set; }
|
||||
public MonitorTypes DefaultMonitorOption { get; set; }
|
||||
public NewItemMonitorTypes DefaultNewItemMonitorOption { get; set; }
|
||||
public HashSet<int> DefaultTags { get; set; }
|
||||
public bool IsCalibreLibrary { get; set; }
|
||||
public string Host { get; set; }
|
||||
|
|
@ -50,6 +51,7 @@ public static RootFolderResource ToResource(this RootFolder model)
|
|||
DefaultMetadataProfileId = model.DefaultMetadataProfileId,
|
||||
DefaultQualityProfileId = model.DefaultQualityProfileId,
|
||||
DefaultMonitorOption = model.DefaultMonitorOption,
|
||||
DefaultNewItemMonitorOption = model.DefaultNewItemMonitorOption,
|
||||
DefaultTags = model.DefaultTags,
|
||||
IsCalibreLibrary = model.IsCalibreLibrary,
|
||||
Host = model.CalibreSettings?.Host,
|
||||
|
|
@ -105,6 +107,7 @@ public static RootFolder ToModel(this RootFolderResource resource)
|
|||
DefaultMetadataProfileId = resource.DefaultMetadataProfileId,
|
||||
DefaultQualityProfileId = resource.DefaultQualityProfileId,
|
||||
DefaultMonitorOption = resource.DefaultMonitorOption,
|
||||
DefaultNewItemMonitorOption = resource.DefaultNewItemMonitorOption,
|
||||
DefaultTags = resource.DefaultTags,
|
||||
IsCalibreLibrary = resource.IsCalibreLibrary,
|
||||
CalibreSettings = cs
|
||||
|
|
|
|||
Loading…
Reference in a new issue