(cherry picked from commit c51ae664aa6e6f5330be67e68476af76c55352f5)pull/2804/head
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;
|
@ -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;
|
@ -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;
|
@ -0,0 +1,7 @@
|
|||||||
|
const monitorNewItemsOptions = [
|
||||||
|
{ key: 'all', value: 'All Albums' },
|
||||||
|
{ 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.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
namespace NzbDrone.Core.Music
|
||||||
|
{
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue