mirror of https://github.com/lidarr/Lidarr
New: Option to control which new artist albums get monitored
(cherry picked from commit c51ae664aa6e6f5330be67e68476af76c55352f5)
This commit is contained in:
parent
d0fd0024e3
commit
2318c43536
|
@ -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 ArtistMonitorNewItemsOptionsPopoverContent() {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('AllAlbums')}
|
||||
data="Monitor all new albums"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('NewAlbums')}
|
||||
data="Monitor new albums released after the newest existing album"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('None')}
|
||||
data="Don't monitor any new albums"
|
||||
/>
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
export default ArtistMonitorNewItemsOptionsPopoverContent;
|
|
@ -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 ArtistMonitoringOptionsPopoverContent() {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('AllAlbums')}
|
||||
data={translate('AllAlbumsData')}
|
||||
/>
|
||||
<>
|
||||
<Alert>
|
||||
This is a one time adjustment to set which albums are monitored
|
||||
</Alert>
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('AllAlbums')}
|
||||
data={translate('AllAlbumsData')}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('FutureAlbums')}
|
||||
data={translate('FutureAlbumsData')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('FutureAlbums')}
|
||||
data={translate('FutureAlbumsData')}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('MissingAlbums')}
|
||||
data={translate('MissingAlbumsData')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('MissingAlbums')}
|
||||
data={translate('MissingAlbumsData')}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('ExistingAlbums')}
|
||||
data={translate('ExistingAlbumsData')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('ExistingAlbums')}
|
||||
data={translate('ExistingAlbumsData')}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('FirstAlbum')}
|
||||
data={translate('FirstAlbumData')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('FirstAlbum')}
|
||||
data={translate('FirstAlbumData')}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('LatestAlbum')}
|
||||
data={translate('LatestAlbumData')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title={translate('LatestAlbum')}
|
||||
data={translate('LatestAlbumData')}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('None')}
|
||||
data={translate('NoneData')}
|
||||
/>
|
||||
</DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('None')}
|
||||
data={translate('NoneData')}
|
||||
/>
|
||||
</DescriptionList>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import MonitorAlbumsSelectInput from 'Components/Form/MonitorAlbumsSelectInput';
|
||||
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 AlbumStudioFooter extends Component {
|
|||
|
||||
this.state = {
|
||||
monitored: NO_CHANGE,
|
||||
monitor: NO_CHANGE
|
||||
monitor: NO_CHANGE,
|
||||
monitorNewItems: NO_CHANGE
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -32,7 +34,8 @@ class AlbumStudioFooter 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 AlbumStudioFooter extends Component {
|
|||
onUpdateSelectedPress = () => {
|
||||
const {
|
||||
monitor,
|
||||
monitored
|
||||
monitored,
|
||||
monitorNewItems
|
||||
} = this.state;
|
||||
|
||||
const changes = {};
|
||||
|
@ -60,6 +64,10 @@ class AlbumStudioFooter extends Component {
|
|||
changes.monitor = monitor;
|
||||
}
|
||||
|
||||
if (monitorNewItems !== NO_CHANGE) {
|
||||
changes.monitorNewItems = monitorNewItems;
|
||||
}
|
||||
|
||||
this.props.onUpdateSelectedPress(changes);
|
||||
};
|
||||
|
||||
|
@ -74,7 +82,8 @@ class AlbumStudioFooter extends Component {
|
|||
|
||||
const {
|
||||
monitored,
|
||||
monitor
|
||||
monitor,
|
||||
monitorNewItems
|
||||
} = this.state;
|
||||
|
||||
const monitoredOptions = [
|
||||
|
@ -83,7 +92,9 @@ class AlbumStudioFooter 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 AlbumStudioFooter extends Component {
|
|||
|
||||
<div className={styles.inputContainer}>
|
||||
<div className={styles.label}>
|
||||
Monitor Albums
|
||||
Monitor Existing Albums
|
||||
</div>
|
||||
|
||||
<MonitorAlbumsSelectInput
|
||||
|
@ -115,6 +126,20 @@ class AlbumStudioFooter extends Component {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<div className={styles.label}>
|
||||
Monitor New Albums
|
||||
</div>
|
||||
|
||||
<MonitorNewItemsSelectInput
|
||||
name="monitorNewItems"
|
||||
value={monitorNewItems}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className={styles.label}>
|
||||
{selectedCount} Artist(s) Selected
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import ArtistMetadataProfilePopoverContent from 'AddArtist/ArtistMetadataProfilePopoverContent';
|
||||
import ArtistMonitorNewItemsOptionsPopoverContent from 'AddArtist/ArtistMonitorNewItemsOptionsPopoverContent';
|
||||
import MoveArtistModal from 'Artist/MoveArtist/MoveArtistModal';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
|
@ -73,6 +74,7 @@ class EditArtistModalContent extends Component {
|
|||
|
||||
const {
|
||||
monitored,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
path,
|
||||
|
@ -101,6 +103,31 @@ class EditArtistModalContent extends Component {
|
|||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('MonitorNewItems')}
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitorNewItems')}
|
||||
body={<ArtistMonitorNewItemsOptionsPopoverContent />}
|
||||
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 artistSettings = _.pick(artist, [
|
||||
'monitored',
|
||||
'monitorNewItems',
|
||||
'qualityProfileId',
|
||||
'metadataProfileId',
|
||||
'path',
|
||||
|
|
|
@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import MoveArtistModal from 'Artist/MoveArtist/MoveArtistModal';
|
||||
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 ArtistEditorFooter extends Component {
|
|||
|
||||
this.state = {
|
||||
monitored: NO_CHANGE,
|
||||
monitorNewItems: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
metadataProfileId: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
|
@ -46,6 +48,7 @@ class ArtistEditorFooter extends Component {
|
|||
if (prevProps.isSaving && !isSaving && !saveError) {
|
||||
this.setState({
|
||||
monitored: NO_CHANGE,
|
||||
monitorNewItems: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
metadataProfileId: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
|
@ -146,6 +149,7 @@ class ArtistEditorFooter extends Component {
|
|||
|
||||
const {
|
||||
monitored,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
rootFolderPath,
|
||||
|
@ -179,6 +183,21 @@ class ArtistEditorFooter extends Component {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<ArtistEditorFooterLabel
|
||||
label={translate('MonitorNewItems')}
|
||||
isSaving={isSaving && monitored !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<MonitorNewItemsSelectInput
|
||||
name="monitorNewItems"
|
||||
value={monitorNewItems}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{
|
||||
columns.map((column) => {
|
||||
const {
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
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';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import Button from 'Components/Link/Button';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
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 } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
const NO_CHANGE = 'noChange';
|
||||
|
||||
class MonitoringOptionsModalContent extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
monitor: NO_CHANGE
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
isSaving,
|
||||
saveError
|
||||
} = prevProps;
|
||||
|
||||
if (prevProps.isSaving && !isSaving && !saveError) {
|
||||
this.setState({
|
||||
monitor: NO_CHANGE
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onInputChange = ({ name, value }) => {
|
||||
this.setState({ [name]: value });
|
||||
};
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onSavePress = () => {
|
||||
const {
|
||||
onSavePress,
|
||||
isSaving
|
||||
} = this.props;
|
||||
const {
|
||||
monitor
|
||||
} = this.state;
|
||||
|
||||
if (monitor !== NO_CHANGE) {
|
||||
onSavePress({ monitor });
|
||||
}
|
||||
|
||||
if (!isSaving) {
|
||||
this.onModalClose();
|
||||
}
|
||||
};
|
||||
|
||||
onModalClose = () => {
|
||||
this.props.onModalClose();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isSaving,
|
||||
onInputChange,
|
||||
onModalClose,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
monitor
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{translate('MonitorAlbum')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>
|
||||
{translate('MonitorAlbumExistingOnlyWarning')}
|
||||
</div>
|
||||
</Alert>
|
||||
|
||||
<Form {...otherProps}>
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('Monitoring')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.MONITOR_ALBUMS_SELECT}
|
||||
name="monitor"
|
||||
value={monitor}
|
||||
includeNoChange={true}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerButton
|
||||
isSpinning={isSaving}
|
||||
onPress={this.onSavePress}
|
||||
>
|
||||
{translate('Save')}
|
||||
</SpinnerButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MonitoringOptionsModalContent.propTypes = {
|
||||
authorId: PropTypes.number.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
MonitoringOptionsModalContent.defaultProps = {
|
||||
isSaving: false
|
||||
};
|
||||
|
||||
export default MonitoringOptionsModalContent;
|
|
@ -14,6 +14,7 @@ import IndexerSelectInputConnector from './IndexerSelectInputConnector';
|
|||
import KeyValueListInput from './KeyValueListInput';
|
||||
import MetadataProfileSelectInputConnector from './MetadataProfileSelectInputConnector';
|
||||
import MonitorAlbumsSelectInput from './MonitorAlbumsSelectInput';
|
||||
import MonitorNewItemsSelectInput from './MonitorNewItemsSelectInput';
|
||||
import NumberInput from './NumberInput';
|
||||
import OAuthInputConnector from './OAuthInputConnector';
|
||||
import PasswordInput from './PasswordInput';
|
||||
|
@ -52,6 +53,9 @@ function getComponent(type) {
|
|||
case inputTypes.MONITOR_ALBUMS_SELECT:
|
||||
return MonitorAlbumsSelectInput;
|
||||
|
||||
case inputTypes.MONITOR_NEW_ITEMS_SELECT:
|
||||
return MonitorNewItemsSelectInput;
|
||||
|
||||
case inputTypes.NUMBER:
|
||||
return NumberInput;
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import monitorNewItemsOptions from 'Utilities/Artist/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 PLAYLIST = 'playlist';
|
||||
export const KEY_VALUE_LIST = 'keyValueList';
|
||||
export const MONITOR_ALBUMS_SELECT = 'monitorAlbumsSelect';
|
||||
export const MONITOR_NEW_ITEMS_SELECT = 'monitorNewItemsSelect';
|
||||
export const NUMBER = 'number';
|
||||
export const OAUTH = 'oauth';
|
||||
export const PASSWORD = 'password';
|
||||
|
@ -31,6 +32,7 @@ export const all = [
|
|||
PLAYLIST,
|
||||
KEY_VALUE_LIST,
|
||||
MONITOR_ALBUMS_SELECT,
|
||||
MONITOR_NEW_ITEMS_SELECT,
|
||||
NUMBER,
|
||||
OAUTH,
|
||||
PASSWORD,
|
||||
|
|
|
@ -85,6 +85,7 @@ class AddNewAlbumModalContentConnector extends Component {
|
|||
foreignAlbumId,
|
||||
rootFolderPath,
|
||||
monitor,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
tags
|
||||
|
@ -94,6 +95,7 @@ class AddNewAlbumModalContentConnector extends Component {
|
|||
foreignAlbumId,
|
||||
rootFolderPath: rootFolderPath.value,
|
||||
monitor: monitor.value,
|
||||
monitorNewItems: monitorNewItems.value,
|
||||
qualityProfileId: qualityProfileId.value,
|
||||
metadataProfileId: metadataProfileId.value,
|
||||
tags: tags.value,
|
||||
|
@ -120,6 +122,7 @@ AddNewAlbumModalContentConnector.propTypes = {
|
|||
foreignAlbumId: PropTypes.string.isRequired,
|
||||
rootFolderPath: PropTypes.object,
|
||||
monitor: PropTypes.object.isRequired,
|
||||
monitorNewItems: PropTypes.object.isRequired,
|
||||
qualityProfileId: PropTypes.object,
|
||||
metadataProfileId: PropTypes.object,
|
||||
noneMetadataProfileId: PropTypes.number.isRequired,
|
||||
|
|
|
@ -57,6 +57,7 @@ class AddNewArtistModalContentConnector extends Component {
|
|||
foreignArtistId,
|
||||
rootFolderPath,
|
||||
monitor,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
tags
|
||||
|
@ -66,6 +67,7 @@ class AddNewArtistModalContentConnector extends Component {
|
|||
foreignArtistId,
|
||||
rootFolderPath: rootFolderPath.value,
|
||||
monitor: monitor.value,
|
||||
monitorNewItems: monitorNewItems.value,
|
||||
qualityProfileId: qualityProfileId.value,
|
||||
metadataProfileId: metadataProfileId.value,
|
||||
tags: tags.value,
|
||||
|
@ -91,6 +93,7 @@ AddNewArtistModalContentConnector.propTypes = {
|
|||
foreignArtistId: 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 ArtistMetadataProfilePopoverContent from 'AddArtist/ArtistMetadataProfilePopoverContent';
|
||||
import ArtistMonitoringOptionsPopoverContent from 'AddArtist/ArtistMonitoringOptionsPopoverContent';
|
||||
import ArtistMonitorNewItemsOptionsPopoverContent from 'AddArtist/ArtistMonitorNewItemsOptionsPopoverContent';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
|
@ -32,6 +33,7 @@ class AddArtistOptionsForm extends Component {
|
|||
const {
|
||||
rootFolderPath,
|
||||
monitor,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
includeNoneMetadataProfile,
|
||||
|
@ -76,11 +78,37 @@ class AddArtistOptionsForm extends Component {
|
|||
<FormInputGroup
|
||||
type={inputTypes.MONITOR_ALBUMS_SELECT}
|
||||
name="monitor"
|
||||
helpText={translate('MonitoringOptionsHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...monitor}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('MonitorNewItems')}
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitorNewItems')}
|
||||
body={<ArtistMonitorNewItemsOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.MONITOR_NEW_ITEMS_SELECT}
|
||||
name="monitorNewItems"
|
||||
helpText={translate('MonitorNewItemsHelpText')}
|
||||
{...monitorNewItems}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('QualityProfile')}
|
||||
|
@ -143,6 +171,7 @@ class AddArtistOptionsForm extends Component {
|
|||
AddArtistOptionsForm.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 ArtistMonitorNewItemsOptionsPopoverContent from 'AddArtist/ArtistMonitorNewItemsOptionsPopoverContent';
|
||||
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';
|
||||
|
@ -74,6 +76,7 @@ function EditImportListModalContent(props) {
|
|||
enableAutomaticAdd,
|
||||
shouldMonitor,
|
||||
rootFolderPath,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
tags,
|
||||
|
@ -112,120 +115,150 @@ 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('RootFolder')}
|
||||
</FormLabel>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableAutomaticAdd"
|
||||
helpText={translate('EnableAutomaticAddHelpText')}
|
||||
{...enableAutomaticAdd}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.ROOT_FOLDER_SELECT}
|
||||
name="rootFolderPath"
|
||||
helpText={translate('RootFolderPathHelpText')}
|
||||
{...rootFolderPath}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Monitor
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('QualityProfile')}
|
||||
</FormLabel>
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitoringOptions')}
|
||||
body={<ImportListMonitoringOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||
name="qualityProfileId"
|
||||
helpText={translate('QualityProfileIdHelpText')}
|
||||
{...qualityProfileId}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="shouldMonitor"
|
||||
values={monitorOptions}
|
||||
helpText={translate('ShouldMonitorHelpText')}
|
||||
{...shouldMonitor}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet>
|
||||
|
||||
<FormGroup className={showMetadataProfile ? undefined : styles.hideMetadataProfile}>
|
||||
<FormLabel>
|
||||
{translate('MetadataProfile')}
|
||||
</FormLabel>
|
||||
<FieldSet legend={translate('AddedArtistSettings')} >
|
||||
<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('LidarrTags')}
|
||||
</FormLabel>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('MonitorNewItems')}
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitorNewItems')}
|
||||
body={<ArtistMonitorNewItemsOptionsPopoverContent />}
|
||||
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 (
|
||||
|
@ -241,7 +274,7 @@ function EditImportListModalContent(props) {
|
|||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</FieldSet>
|
||||
}
|
||||
|
||||
</Form>
|
||||
|
|
|
@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React from 'react';
|
||||
import ArtistMetadataProfilePopoverContent from 'AddArtist/ArtistMetadataProfilePopoverContent';
|
||||
import ArtistMonitoringOptionsPopoverContent from 'AddArtist/ArtistMonitoringOptionsPopoverContent';
|
||||
import ArtistMonitorNewItemsOptionsPopoverContent from 'AddArtist/ArtistMonitorNewItemsOptionsPopoverContent';
|
||||
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
|
||||
} = item;
|
||||
|
||||
|
@ -99,7 +101,7 @@ function EditRootFolderModalContent(props) {
|
|||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Monitor
|
||||
{translate('Monitor')}
|
||||
|
||||
<Popover
|
||||
anchor={
|
||||
|
@ -124,6 +126,31 @@ function EditRootFolderModalContent(props) {
|
|||
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('MonitorNewItems')}
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
className={styles.labelIcon}
|
||||
name={icons.INFO}
|
||||
/>
|
||||
}
|
||||
title={translate('MonitorNewItems')}
|
||||
body={<ArtistMonitorNewItemsOptionsPopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.MONITOR_NEW_ITEMS_SELECT}
|
||||
name="defaultNewItemMonitorOption"
|
||||
{...defaultNewItemMonitorOption}
|
||||
onChange={onInputChange}
|
||||
helpText={translate('MonitorNewItemsHelpText')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('QualityProfile')}
|
||||
|
|
|
@ -102,7 +102,8 @@ export const actionHandlers = handleThunks({
|
|||
const {
|
||||
artistIds,
|
||||
monitored,
|
||||
monitor
|
||||
monitor,
|
||||
monitorNewItems
|
||||
} = payload;
|
||||
|
||||
const artist = [];
|
||||
|
@ -127,7 +128,8 @@ export const actionHandlers = handleThunks({
|
|||
method: 'POST',
|
||||
data: JSON.stringify({
|
||||
artist,
|
||||
monitoringOptions: { monitor }
|
||||
monitoringOptions: { monitor },
|
||||
monitorNewItems
|
||||
}),
|
||||
dataType: 'json'
|
||||
}).request;
|
||||
|
|
|
@ -3,6 +3,7 @@ function getNewArtist(artist, payload) {
|
|||
const {
|
||||
rootFolderPath,
|
||||
monitor,
|
||||
monitorNewItems,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
artistType,
|
||||
|
@ -17,6 +18,7 @@ function getNewArtist(artist, payload) {
|
|||
|
||||
artist.addOptions = addOptions;
|
||||
artist.monitored = true;
|
||||
artist.monitorNewItems = monitorNewItems;
|
||||
artist.qualityProfileId = qualityProfileId;
|
||||
artist.metadataProfileId = metadataProfileId;
|
||||
artist.rootFolderPath = rootFolderPath;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
const monitorNewItemsOptions = [
|
||||
{ key: 'all', value: 'All Albums' },
|
||||
{ key: 'none', value: 'None' },
|
||||
{ key: 'new', value: 'New' }
|
||||
];
|
||||
|
||||
export default monitorNewItemsOptions;
|
|
@ -36,10 +36,15 @@ namespace Lidarr.Api.V1.AlbumStudio
|
|||
artist.Monitored = false;
|
||||
}
|
||||
|
||||
if (request.MonitorNewItems.HasValue)
|
||||
{
|
||||
artist.MonitorNewItems = request.MonitorNewItems.Value;
|
||||
}
|
||||
|
||||
_albumMonitoredService.SetAlbumMonitoredStatus(artist, request.MonitoringOptions);
|
||||
}
|
||||
|
||||
return Accepted();
|
||||
return Accepted(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,6 @@ namespace Lidarr.Api.V1.AlbumStudio
|
|||
{
|
||||
public List<AlbumStudioArtistResource> Artist { get; set; }
|
||||
public MonitoringOptions MonitoringOptions { get; set; }
|
||||
public NewItemMonitorTypes? MonitorNewItems { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,11 @@ namespace Lidarr.Api.V1.Artist
|
|||
artist.Monitored = resource.Monitored.Value;
|
||||
}
|
||||
|
||||
if (resource.MonitorNewItems.HasValue)
|
||||
{
|
||||
artist.MonitorNewItems = resource.MonitorNewItems.Value;
|
||||
}
|
||||
|
||||
if (resource.QualityProfileId.HasValue)
|
||||
{
|
||||
artist.QualityProfileId = resource.QualityProfileId.Value;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace Lidarr.Api.V1.Artist
|
||||
{
|
||||
|
@ -6,6 +7,7 @@ namespace Lidarr.Api.V1.Artist
|
|||
{
|
||||
public List<int> ArtistIds { 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; }
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace Lidarr.Api.V1.Artist
|
|||
|
||||
//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 +92,7 @@ namespace Lidarr.Api.V1.Artist
|
|||
Links = model.Metadata.Value.Links,
|
||||
|
||||
Monitored = model.Monitored,
|
||||
MonitorNewItems = model.MonitorNewItems,
|
||||
|
||||
CleanName = model.CleanName,
|
||||
ForeignArtistId = model.Metadata.Value.ForeignArtistId,
|
||||
|
@ -138,6 +140,7 @@ namespace Lidarr.Api.V1.Artist
|
|||
MetadataProfileId = resource.MetadataProfileId,
|
||||
|
||||
Monitored = resource.Monitored,
|
||||
MonitorNewItems = resource.MonitorNewItems,
|
||||
|
||||
CleanName = resource.CleanName,
|
||||
RootFolderPath = resource.RootFolderPath,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using NzbDrone.Core.ImportLists;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace Lidarr.Api.V1.ImportLists
|
||||
{
|
||||
|
@ -7,6 +8,7 @@ namespace Lidarr.Api.V1.ImportLists
|
|||
public bool EnableAutomaticAdd { get; set; }
|
||||
public ImportListMonitorType ShouldMonitor { 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; }
|
||||
|
@ -27,6 +29,7 @@ namespace Lidarr.Api.V1.ImportLists
|
|||
resource.EnableAutomaticAdd = definition.EnableAutomaticAdd;
|
||||
resource.ShouldMonitor = definition.ShouldMonitor;
|
||||
resource.RootFolderPath = definition.RootFolderPath;
|
||||
resource.MonitorNewItems = definition.MonitorNewItems;
|
||||
resource.QualityProfileId = definition.ProfileId;
|
||||
resource.MetadataProfileId = definition.MetadataProfileId;
|
||||
resource.ListType = definition.ListType;
|
||||
|
@ -47,6 +50,7 @@ namespace Lidarr.Api.V1.ImportLists
|
|||
definition.EnableAutomaticAdd = resource.EnableAutomaticAdd;
|
||||
definition.ShouldMonitor = resource.ShouldMonitor;
|
||||
definition.RootFolderPath = resource.RootFolderPath;
|
||||
definition.MonitorNewItems = resource.MonitorNewItems;
|
||||
definition.ProfileId = resource.QualityProfileId;
|
||||
definition.MetadataProfileId = resource.MetadataProfileId;
|
||||
definition.ListType = resource.ListType;
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Lidarr.Api.V1.RootFolders
|
|||
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 Accessible { get; set; }
|
||||
|
@ -38,6 +39,7 @@ namespace Lidarr.Api.V1.RootFolders
|
|||
DefaultMetadataProfileId = model.DefaultMetadataProfileId,
|
||||
DefaultQualityProfileId = model.DefaultQualityProfileId,
|
||||
DefaultMonitorOption = model.DefaultMonitorOption,
|
||||
DefaultNewItemMonitorOption = model.DefaultNewItemMonitorOption,
|
||||
DefaultTags = model.DefaultTags,
|
||||
|
||||
Accessible = model.Accessible,
|
||||
|
@ -62,7 +64,8 @@ namespace Lidarr.Api.V1.RootFolders
|
|||
DefaultMetadataProfileId = resource.DefaultMetadataProfileId,
|
||||
DefaultQualityProfileId = resource.DefaultQualityProfileId,
|
||||
DefaultMonitorOption = resource.DefaultMonitorOption,
|
||||
DefaultTags = resource.DefaultTags
|
||||
DefaultNewItemMonitorOption = resource.DefaultNewItemMonitorOption,
|
||||
DefaultTags = resource.DefaultTags,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.AlbumTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class MonitorNewAlbumServiceFixture : CoreTest<MonitorNewAlbumService>
|
||||
{
|
||||
private List<Album> _albums;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_albums = Builder<Album>.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 album in _albums)
|
||||
{
|
||||
Subject.ShouldMonitorNewAlbum(album, _albums, NewItemMonitorTypes.All).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_monitor_with_none()
|
||||
{
|
||||
foreach (var album in _albums)
|
||||
{
|
||||
Subject.ShouldMonitorNewAlbum(album, _albums, NewItemMonitorTypes.None).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_monitor_new_with_new()
|
||||
{
|
||||
Subject.ShouldMonitorNewAlbum(_albums[0], _albums, NewItemMonitorTypes.New).Should().BeTrue();
|
||||
|
||||
foreach (var album in _albums.Skip(1))
|
||||
{
|
||||
Subject.ShouldMonitorNewAlbum(album, _albums, NewItemMonitorTypes.New).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -74,6 +74,10 @@ namespace NzbDrone.Core.Test.MusicTests
|
|||
Mocker.GetMock<IRootFolderService>()
|
||||
.Setup(x => x.All())
|
||||
.Returns(new List<RootFolder>());
|
||||
|
||||
Mocker.GetMock<IMonitorNewAlbumService>()
|
||||
.Setup(x => x.ShouldMonitorNewAlbum(It.IsAny<Album>(), It.IsAny<List<Album>>(), It.IsAny<NewItemMonitorTypes>()))
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
private void GivenNewArtistInfo(Artist artist)
|
||||
|
@ -143,7 +147,30 @@ namespace NzbDrone.Core.Test.MusicTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void should_log_error_and_delete_if_musicbrainz_id_not_found_and_artist_has_no_files()
|
||||
public void should_call_new_album_monitor_service_when_adding_album()
|
||||
{
|
||||
var newAlbum = Builder<Album>.CreateNew()
|
||||
.With(x => x.Id = 0)
|
||||
.With(x => x.ForeignAlbumId = "3")
|
||||
.Build();
|
||||
_remoteAlbums.Add(newAlbum);
|
||||
|
||||
var newAuthorInfo = _artist.JsonClone();
|
||||
newAuthorInfo.Metadata = _artist.Metadata.Value.JsonClone();
|
||||
newAuthorInfo.Albums = _remoteAlbums;
|
||||
|
||||
GivenNewArtistInfo(newAuthorInfo);
|
||||
GivenAlbumsForRefresh(_albums);
|
||||
AllowArtistUpdate();
|
||||
|
||||
Subject.Execute(new RefreshArtistCommand(_artist.Id));
|
||||
|
||||
Mocker.GetMock<IMonitorNewAlbumService>()
|
||||
.Verify(x => x.ShouldMonitorNewAlbum(newAlbum, _albums, _artist.MonitorNewItems), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_log_error_and_delete_if_musicbrainz_id_not_found_and_author_has_no_files()
|
||||
{
|
||||
Mocker.GetMock<IArtistService>()
|
||||
.Setup(x => x.DeleteArtist(It.IsAny<int>(), It.IsAny<bool>(), It.IsAny<bool>()));
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(56)]
|
||||
public class AddNewItemMonitorType : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Artists").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.Music;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists
|
||||
|
@ -6,6 +7,7 @@ namespace NzbDrone.Core.ImportLists
|
|||
{
|
||||
public bool EnableAutomaticAdd { get; set; }
|
||||
public ImportListMonitorType ShouldMonitor { get; set; }
|
||||
public NewItemMonitorTypes MonitorNewItems { get; set; }
|
||||
public int ProfileId { get; set; }
|
||||
public int MetadataProfileId { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
|
|
|
@ -270,6 +270,7 @@ namespace NzbDrone.Core.ImportLists
|
|||
Name = report.Artist
|
||||
},
|
||||
Monitored = monitored,
|
||||
MonitorNewItems = importList.MonitorNewItems,
|
||||
RootFolderPath = importList.RootFolderPath,
|
||||
QualityProfileId = importList.ProfileId,
|
||||
MetadataProfileId = importList.MetadataProfileId,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"About": "About",
|
||||
"Absolute": "Absolute",
|
||||
"Actions": "Actions",
|
||||
"AddedArtistSettings": "Added Artist Settings",
|
||||
"AddImportListExclusionHelpText": "Prevent artist from being added to Lidarr by Import lists",
|
||||
"AddingTag": "Adding tag",
|
||||
"AddListExclusion": "Add List Exclusion",
|
||||
|
@ -113,7 +114,7 @@
|
|||
"DBMigration": "DB Migration",
|
||||
"DefaultLidarrTags": "Default Lidarr Tags",
|
||||
"DefaultMetadataProfileIdHelpText": "Default Metadata Profile for artists detected in this folder",
|
||||
"DefaultMonitorOptionHelpText": "Default Monitoring Options for albums by artists detected in this folder",
|
||||
"DefaultMonitorOptionHelpText": "Which albums should be monitored on initial add for artists detected in this folder",
|
||||
"DefaultQualityProfileIdHelpText": "Default Quality Profile for artists detected in this folder",
|
||||
"DefaultTagsHelpText": "Default Lidarr Tags for artists detected in this folder",
|
||||
"DelayingDownloadUntilInterp": "Delaying download until {0} at {1}",
|
||||
|
@ -261,7 +262,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",
|
||||
"IncludeUnknownArtistItemsHelpText": "Show items without a artist in the queue, this could include removed artists, movies or anything else in Lidarr's category",
|
||||
|
@ -348,10 +350,15 @@
|
|||
"MissingTracksArtistMonitored": "Missing Tracks (Artist monitored)",
|
||||
"MissingTracksArtistNotMonitored": "Missing Tracks (Artist not monitored)",
|
||||
"Mode": "Mode",
|
||||
"MonitorAlbumExistingOnlyWarning": "This is a one off adjustment of the monitored setting for each album. Use the option under Artist/Edit to control what happens for newly added albums",
|
||||
"MonitorArtist": "Monitor Artist",
|
||||
"Monitored": "Monitored",
|
||||
"MonitoredHelpText": "Download monitored albums from this artist",
|
||||
"MonitoringOptions": "Monitoring Options",
|
||||
"MonitoringOptionsHelpText": "Which albums should be monitored after the artist is added (one-time adjustment)",
|
||||
"MonitorNewItems": "Monitor New Albums",
|
||||
"MonitorNewItemsHelpText": "Which new albums should be monitored",
|
||||
"MonoVersion": "Mono Version",
|
||||
"MoreInfo": "More Info",
|
||||
"MultiDiscTrackFormat": "Multi Disc Track Format",
|
||||
"MusicBrainzAlbumID": "MusicBrainz Album ID",
|
||||
|
@ -366,6 +373,7 @@
|
|||
"NamingSettings": "Naming Settings",
|
||||
"NETCore": ".NET",
|
||||
"New": "New",
|
||||
"NewAlbums": "New Albums",
|
||||
"NoBackupsAreAvailable": "No backups are available",
|
||||
"NoHistory": "No history.",
|
||||
"NoLeaveIt": "No, Leave It",
|
||||
|
|
|
@ -337,6 +337,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
|
|||
artist.MetadataProfileId = rootFolder.DefaultMetadataProfileId;
|
||||
artist.QualityProfileId = rootFolder.DefaultQualityProfileId;
|
||||
artist.Monitored = rootFolder.DefaultMonitorOption != MonitorTypes.None;
|
||||
artist.MonitorNewItems = rootFolder.DefaultNewItemMonitorOption;
|
||||
artist.Tags = rootFolder.DefaultTags;
|
||||
artist.AddOptions = new AddArtistOptions
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace NzbDrone.Core.Music
|
|||
public string CleanName { get; set; }
|
||||
public string SortName { 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 @@ namespace NzbDrone.Core.Music
|
|||
Id = other.Id;
|
||||
ArtistMetadataId = other.ArtistMetadataId;
|
||||
Monitored = other.Monitored;
|
||||
MonitorNewItems = other.MonitorNewItems;
|
||||
LastInfoSync = other.LastInfoSync;
|
||||
Path = other.Path;
|
||||
RootFolderPath = other.RootFolderPath;
|
||||
|
@ -93,6 +95,7 @@ namespace NzbDrone.Core.Music
|
|||
AddOptions = other.AddOptions;
|
||||
RootFolderPath = other.RootFolderPath;
|
||||
Monitored = other.Monitored;
|
||||
MonitorNewItems = other.MonitorNewItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
namespace NzbDrone.Core.Music
|
||||
{
|
||||
public enum MonitorTypes
|
||||
{
|
||||
All,
|
||||
Future,
|
||||
Missing,
|
||||
Existing,
|
||||
Latest,
|
||||
First,
|
||||
None,
|
||||
Unknown
|
||||
}
|
||||
}
|
|
@ -14,16 +14,4 @@ namespace NzbDrone.Core.Music
|
|||
public List<string> AlbumsToMonitor { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
}
|
||||
|
||||
public enum MonitorTypes
|
||||
{
|
||||
All,
|
||||
Future,
|
||||
Missing,
|
||||
Existing,
|
||||
Latest,
|
||||
First,
|
||||
None,
|
||||
Unknown
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
namespace NzbDrone.Core.Music
|
||||
{
|
||||
public enum NewItemMonitorTypes
|
||||
{
|
||||
All,
|
||||
None,
|
||||
New
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
|
||||
namespace NzbDrone.Core.Music
|
||||
{
|
||||
public interface IMonitorNewAlbumService
|
||||
{
|
||||
bool ShouldMonitorNewAlbum(Album addedAlbum, List<Album> existingAlbums, NewItemMonitorTypes monitorNewItems);
|
||||
}
|
||||
|
||||
public class MonitorNewAlbumService : IMonitorNewAlbumService
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MonitorNewAlbumService(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool ShouldMonitorNewAlbum(Album addedAlbum, List<Album> existingAlbums, NewItemMonitorTypes monitorNewItems)
|
||||
{
|
||||
if (monitorNewItems == NewItemMonitorTypes.None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (monitorNewItems == NewItemMonitorTypes.All)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (monitorNewItems == NewItemMonitorTypes.New)
|
||||
{
|
||||
var newest = existingAlbums.OrderByDescending(x => x.ReleaseDate ?? DateTime.MinValue).FirstOrDefault()?.ReleaseDate ?? DateTime.MinValue;
|
||||
|
||||
return (addedAlbum.ReleaseDate ?? DateTime.MinValue) >= newest;
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"Unknown new item monitor type {monitorNewItems}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -103,10 +103,8 @@ namespace NzbDrone.Core.Music
|
|||
local.AlbumRelease = entity;
|
||||
local.AlbumReleaseId = entity.Id;
|
||||
local.ArtistMetadataId = remote.ArtistMetadata.Value.Id;
|
||||
remote.Id = local.Id;
|
||||
remote.TrackFileId = local.TrackFileId;
|
||||
remote.AlbumReleaseId = local.AlbumReleaseId;
|
||||
remote.ArtistMetadataId = local.ArtistMetadataId;
|
||||
|
||||
remote.UseDbFieldsFrom(local);
|
||||
}
|
||||
|
||||
protected override void AddChildren(List<Track> children)
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace NzbDrone.Core.Music
|
|||
private readonly IHistoryService _historyService;
|
||||
private readonly IRootFolderService _rootFolderService;
|
||||
private readonly ICheckIfArtistShouldBeRefreshed _checkIfArtistShouldBeRefreshed;
|
||||
private readonly IMonitorNewAlbumService _monitorNewAlbumService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IImportListExclusionService _importListExclusionService;
|
||||
private readonly Logger _logger;
|
||||
|
@ -50,6 +51,7 @@ namespace NzbDrone.Core.Music
|
|||
IHistoryService historyService,
|
||||
IRootFolderService rootFolderService,
|
||||
ICheckIfArtistShouldBeRefreshed checkIfArtistShouldBeRefreshed,
|
||||
IMonitorNewAlbumService monitorNewAlbumService,
|
||||
IConfigService configService,
|
||||
IImportListExclusionService importListExclusionService,
|
||||
Logger logger)
|
||||
|
@ -65,6 +67,7 @@ namespace NzbDrone.Core.Music
|
|||
_historyService = historyService;
|
||||
_rootFolderService = rootFolderService;
|
||||
_checkIfArtistShouldBeRefreshed = checkIfArtistShouldBeRefreshed;
|
||||
_monitorNewAlbumService = monitorNewAlbumService;
|
||||
_configService = configService;
|
||||
_importListExclusionService = importListExclusionService;
|
||||
_logger = logger;
|
||||
|
@ -238,6 +241,15 @@ namespace NzbDrone.Core.Music
|
|||
local.ArtistMetadataId = entity.Metadata.Value.Id;
|
||||
}
|
||||
|
||||
protected override void ProcessChildren(Artist entity, SortedChildren children)
|
||||
{
|
||||
foreach (var album in children.Added)
|
||||
{
|
||||
// all existing child albums count as updated as we don't have proper data yet.
|
||||
album.Monitored = _monitorNewAlbumService.ShouldMonitorNewAlbum(album, children.Updated, entity.MonitorNewItems);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AddChildren(List<Album> children)
|
||||
{
|
||||
_albumService.InsertMany(children);
|
||||
|
|
|
@ -93,6 +93,11 @@ namespace NzbDrone.Core.Music
|
|||
|
||||
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, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate);
|
||||
|
||||
|
@ -274,6 +279,8 @@ namespace NzbDrone.Core.Music
|
|||
sortedChildren.Merged.Count,
|
||||
sortedChildren.Deleted.Count);
|
||||
|
||||
ProcessChildren(entity, sortedChildren);
|
||||
|
||||
// Add in the new children (we have checked that foreign IDs don't clash)
|
||||
AddChildren(sortedChildren.Added);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace NzbDrone.Core.RootFolders
|
|||
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 Accessible { get; set; }
|
||||
|
|
Loading…
Reference in New Issue