{selectedCount} Author(s) Selected
diff --git a/frontend/src/Components/Form/FormInputGroup.js b/frontend/src/Components/Form/FormInputGroup.js
index ef2729bc0..01b004b45 100644
--- a/frontend/src/Components/Form/FormInputGroup.js
+++ b/frontend/src/Components/Form/FormInputGroup.js
@@ -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;
diff --git a/frontend/src/Components/Form/MonitorNewItemsSelectInput.js b/frontend/src/Components/Form/MonitorNewItemsSelectInput.js
new file mode 100644
index 000000000..6069b5511
--- /dev/null
+++ b/frontend/src/Components/Form/MonitorNewItemsSelectInput.js
@@ -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 (
+
+ );
+}
+
+MonitorNewItemsSelectInput.propTypes = {
+ includeNoChange: PropTypes.bool.isRequired,
+ includeMixed: PropTypes.bool.isRequired,
+ onChange: PropTypes.func.isRequired
+};
+
+MonitorNewItemsSelectInput.defaultProps = {
+ includeNoChange: false,
+ includeMixed: false
+};
+
+export default MonitorNewItemsSelectInput;
diff --git a/frontend/src/Helpers/Props/inputTypes.js b/frontend/src/Helpers/Props/inputTypes.js
index 6fe18d0b8..887535664 100644
--- a/frontend/src/Helpers/Props/inputTypes.js
+++ b/frontend/src/Helpers/Props/inputTypes.js
@@ -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,
diff --git a/frontend/src/Search/Author/AddNewAuthorModalContentConnector.js b/frontend/src/Search/Author/AddNewAuthorModalContentConnector.js
index 51ce83bd0..60886520b 100644
--- a/frontend/src/Search/Author/AddNewAuthorModalContentConnector.js
+++ b/frontend/src/Search/Author/AddNewAuthorModalContentConnector.js
@@ -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,
diff --git a/frontend/src/Search/Book/AddNewBookModalContentConnector.js b/frontend/src/Search/Book/AddNewBookModalContentConnector.js
index a095eed0e..c39f55ce0 100644
--- a/frontend/src/Search/Book/AddNewBookModalContentConnector.js
+++ b/frontend/src/Search/Book/AddNewBookModalContentConnector.js
@@ -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,
diff --git a/frontend/src/Search/Common/AddAuthorOptionsForm.js b/frontend/src/Search/Common/AddAuthorOptionsForm.js
index 7b2ab4c17..e97b2fa0d 100644
--- a/frontend/src/Search/Common/AddAuthorOptionsForm.js
+++ b/frontend/src/Search/Common/AddAuthorOptionsForm.js
@@ -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 {
+
+
+ {translate('MonitorNewItems')}
+
+ }
+ title={translate('MonitorNewItems')}
+ body={}
+ position={tooltipPositions.RIGHT}
+ />
+
+
+
+
+
{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,
diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js
index 916999bda..9ff064e25 100644
--- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js
+++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js
@@ -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}
}
-
-
- {translate('Name')}
-
-
-
-
-
-
-
- {translate('EnableAutomaticAdd')}
-
-
-
-
-
-
-
- Monitor
-
-
- }
- title={translate('MonitoringOptions')}
- body={}
- position={tooltipPositions.RIGHT}
+
+
+
+
-
-
-
-
-
-
- {translate('ShouldMonitorExisting')}
-
-
-
-
-
-
-
- {translate('SearchForNewItems')}
-
-
-
-
-
-
-
- {translate('RootFolder')}
-
-
-
-
-
-
-
- {translate('QualityProfile')}
-
-
-
-
-
-
-
- {translate('MetadataProfile')}
-
-
-
-
-
-
-
- {translate('ReadarrTags')}
-
-
-
-
+
+
{
!!fields && !!fields.length &&
-
+
+
}
diff --git a/frontend/src/Settings/MediaManagement/RootFolder/EditRootFolderModalContent.js b/frontend/src/Settings/MediaManagement/RootFolder/EditRootFolderModalContent.js
index a1e13c534..4d57b9d99 100644
--- a/frontend/src/Settings/MediaManagement/RootFolder/EditRootFolderModalContent.js
+++ b/frontend/src/Settings/MediaManagement/RootFolder/EditRootFolderModalContent.js
@@ -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) {
- Monitor
+ {translate('Monitor')}
+
+
+ {translate('MonitorNewItems')}
+
+ }
+ title={translate('MonitorNewItems')}
+ body={}
+ position={tooltipPositions.RIGHT}
+ />
+
+
+
+
+
{translate('QualityProfile')}
diff --git a/frontend/src/Store/Actions/bookshelfActions.js b/frontend/src/Store/Actions/bookshelfActions.js
index 5e5f4cd08..d62b61c2d 100644
--- a/frontend/src/Store/Actions/bookshelfActions.js
+++ b/frontend/src/Store/Actions/bookshelfActions.js
@@ -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,
diff --git a/frontend/src/Utilities/Author/getNewAuthor.js b/frontend/src/Utilities/Author/getNewAuthor.js
index bea2ca0af..de774474e 100644
--- a/frontend/src/Utilities/Author/getNewAuthor.js
+++ b/frontend/src/Utilities/Author/getNewAuthor.js
@@ -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;
diff --git a/frontend/src/Utilities/Author/monitorNewItemsOptions.js b/frontend/src/Utilities/Author/monitorNewItemsOptions.js
new file mode 100644
index 000000000..839cd8563
--- /dev/null
+++ b/frontend/src/Utilities/Author/monitorNewItemsOptions.js
@@ -0,0 +1,7 @@
+const monitorNewItemsOptions = [
+ { key: 'all', value: 'All Books' },
+ { key: 'none', value: 'None' },
+ { key: 'new', value: 'New' }
+];
+
+export default monitorNewItemsOptions;
diff --git a/src/NzbDrone.Core.Test/MusicTests/MonitorNewBookServiceFixture.cs b/src/NzbDrone.Core.Test/MusicTests/MonitorNewBookServiceFixture.cs
new file mode 100644
index 000000000..eb6082eab
--- /dev/null
+++ b/src/NzbDrone.Core.Test/MusicTests/MonitorNewBookServiceFixture.cs
@@ -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
+ {
+ private List _books;
+
+ [SetUp]
+ public void Setup()
+ {
+ _books = Builder.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();
+ }
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs b/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs
index eedf849cb..474e24a2f 100644
--- a/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs
@@ -62,7 +62,7 @@ namespace NzbDrone.Core.Test.MusicTests
Mocker.GetMock()
.Setup(s => s.FilterBooks(It.IsAny(), It.IsAny()))
- .Returns(_books);
+ .Returns(_remoteBooks);
Mocker.GetMock()
.Setup(s => s.GetAuthorAndBooks(It.IsAny(), It.IsAny()))
@@ -83,6 +83,10 @@ namespace NzbDrone.Core.Test.MusicTests
Mocker.GetMock()
.Setup(x => x.All())
.Returns(new List());
+
+ Mocker.GetMock()
+ .Setup(x => x.ShouldMonitorNewBook(It.IsAny(), It.IsAny>(), It.IsAny()))
+ .Returns(true);
}
private void GivenNewAuthorInfo(Author author)
@@ -151,6 +155,29 @@ namespace NzbDrone.Core.Test.MusicTests
VerifyEventPublished();
}
+ [Test]
+ public void should_call_new_book_monitor_service_when_adding_book()
+ {
+ var newBook = Builder.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()
+ .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()
{
diff --git a/src/NzbDrone.Core/Books/Model/Author.cs b/src/NzbDrone.Core/Books/Model/Author.cs
index c14d70d9d..9a5364e57 100644
--- a/src/NzbDrone.Core/Books/Model/Author.cs
+++ b/src/NzbDrone.Core/Books/Model/Author.cs
@@ -20,6 +20,7 @@ namespace NzbDrone.Core.Books
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 @@ namespace NzbDrone.Core.Books
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 @@ namespace NzbDrone.Core.Books
AddOptions = other.AddOptions;
RootFolderPath = other.RootFolderPath;
Monitored = other.Monitored;
+ MonitorNewItems = other.MonitorNewItems;
}
}
}
diff --git a/src/NzbDrone.Core/Books/Model/MonitorTypes.cs b/src/NzbDrone.Core/Books/Model/MonitorTypes.cs
new file mode 100644
index 000000000..1eee71579
--- /dev/null
+++ b/src/NzbDrone.Core/Books/Model/MonitorTypes.cs
@@ -0,0 +1,14 @@
+namespace NzbDrone.Core.Books
+{
+ public enum MonitorTypes
+ {
+ All,
+ Future,
+ Missing,
+ Existing,
+ Latest,
+ First,
+ None,
+ Unknown
+ }
+}
diff --git a/src/NzbDrone.Core/Books/Model/MonitoringOptions.cs b/src/NzbDrone.Core/Books/Model/MonitoringOptions.cs
index c97d4e4a4..fd824c37f 100644
--- a/src/NzbDrone.Core/Books/Model/MonitoringOptions.cs
+++ b/src/NzbDrone.Core/Books/Model/MonitoringOptions.cs
@@ -14,16 +14,4 @@ namespace NzbDrone.Core.Books
public List BooksToMonitor { get; set; }
public bool Monitored { get; set; }
}
-
- public enum MonitorTypes
- {
- All,
- Future,
- Missing,
- Existing,
- Latest,
- First,
- None,
- Unknown
- }
}
diff --git a/src/NzbDrone.Core/Books/Model/NewItemMonitorTypes.cs b/src/NzbDrone.Core/Books/Model/NewItemMonitorTypes.cs
new file mode 100644
index 000000000..a93a012f3
--- /dev/null
+++ b/src/NzbDrone.Core/Books/Model/NewItemMonitorTypes.cs
@@ -0,0 +1,9 @@
+namespace NzbDrone.Core.Books
+{
+ public enum NewItemMonitorTypes
+ {
+ All,
+ None,
+ New
+ }
+}
diff --git a/src/NzbDrone.Core/Books/Services/MonitorNewBookService.cs b/src/NzbDrone.Core/Books/Services/MonitorNewBookService.cs
new file mode 100644
index 000000000..2e1325a78
--- /dev/null
+++ b/src/NzbDrone.Core/Books/Services/MonitorNewBookService.cs
@@ -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 existingBooks, NewItemMonitorTypes author);
+ }
+
+ public class MonitorNewBookService : IMonitorNewBookService
+ {
+ private readonly Logger _logger;
+
+ public MonitorNewBookService(Logger logger)
+ {
+ _logger = logger;
+ }
+
+ public bool ShouldMonitorNewBook(Book addedBook, List 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}");
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/Books/Services/RefreshAuthorService.cs b/src/NzbDrone.Core/Books/Services/RefreshAuthorService.cs
index 135d05308..85d284634 100644
--- a/src/NzbDrone.Core/Books/Services/RefreshAuthorService.cs
+++ b/src/NzbDrone.Core/Books/Services/RefreshAuthorService.cs
@@ -42,6 +42,7 @@ namespace NzbDrone.Core.Books
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 @@ namespace NzbDrone.Core.Books
IHistoryService historyService,
IRootFolderService rootFolderService,
ICheckIfAuthorShouldBeRefreshed checkIfAuthorShouldBeRefreshed,
+ IMonitorNewBookService monitorNewBookService,
IConfigService configService,
IImportListExclusionService importListExclusionService,
Logger logger)
@@ -76,6 +78,7 @@ namespace NzbDrone.Core.Books
_historyService = historyService;
_rootFolderService = rootFolderService;
_checkIfAuthorShouldBeRefreshed = checkIfAuthorShouldBeRefreshed;
+ _monitorNewBookService = monitorNewBookService;
_configService = configService;
_importListExclusionService = importListExclusionService;
_logger = logger;
@@ -264,6 +267,14 @@ namespace NzbDrone.Core.Books
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 children)
{
_bookService.InsertMany(children);
diff --git a/src/NzbDrone.Core/Books/Services/RefreshEntityServiceBase.cs b/src/NzbDrone.Core/Books/Services/RefreshEntityServiceBase.cs
index a5d4e199e..3405f5237 100644
--- a/src/NzbDrone.Core/Books/Services/RefreshEntityServiceBase.cs
+++ b/src/NzbDrone.Core/Books/Services/RefreshEntityServiceBase.cs
@@ -93,6 +93,11 @@ namespace NzbDrone.Core.Books
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 children);
protected abstract bool RefreshChildren(SortedChildren localChildren, List remoteChildren, Author remoteData, bool forceChildRefresh, bool forceUpdateFileTags, DateTime? lastUpdate);
@@ -277,6 +282,8 @@ namespace NzbDrone.Core.Books
sortedChildren.Deleted.Count);
}
+ ProcessChildren(entity, sortedChildren);
+
// Add in the new children (we have checked that foreign IDs don't clash)
AddChildren(sortedChildren.Added);
diff --git a/src/NzbDrone.Core/Datastore/Migration/019_add_new_item_monitor_type.cs b/src/NzbDrone.Core/Datastore/Migration/019_add_new_item_monitor_type.cs
new file mode 100644
index 000000000..7c9a6bc79
--- /dev/null
+++ b/src/NzbDrone.Core/Datastore/Migration/019_add_new_item_monitor_type.cs
@@ -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);
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs b/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs
index 849b5200b..9f83c9560 100644
--- a/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs
+++ b/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs
@@ -1,3 +1,4 @@
+using NzbDrone.Core.Books;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.ImportLists
@@ -8,6 +9,7 @@ namespace NzbDrone.Core.ImportLists
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; }
diff --git a/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs b/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs
index f6dfbdf0c..2c7faae5d 100644
--- a/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs
+++ b/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs
@@ -227,6 +227,7 @@ namespace NzbDrone.Core.ImportLists
var toAddAuthor = new Author
{
Monitored = monitored,
+ MonitorNewItems = importList.MonitorNewItems,
RootFolderPath = importList.RootFolderPath,
QualityProfileId = importList.ProfileId,
MetadataProfileId = importList.MetadataProfileId,
diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json
index 235d254b6..3e7b6df61 100644
--- a/src/NzbDrone.Core/Localization/Core/en.json
+++ b/src/NzbDrone.Core/Localization/Core/en.json
@@ -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",
diff --git a/src/NzbDrone.Core/MediaFiles/BookImport/ImportApprovedBooks.cs b/src/NzbDrone.Core/MediaFiles/BookImport/ImportApprovedBooks.cs
index f00bf134f..8a91c95bf 100644
--- a/src/NzbDrone.Core/MediaFiles/BookImport/ImportApprovedBooks.cs
+++ b/src/NzbDrone.Core/MediaFiles/BookImport/ImportApprovedBooks.cs
@@ -328,6 +328,7 @@ namespace NzbDrone.Core.MediaFiles.BookImport
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
{
diff --git a/src/NzbDrone.Core/RootFolders/RootFolder.cs b/src/NzbDrone.Core/RootFolders/RootFolder.cs
index cdc8c7947..d41930671 100644
--- a/src/NzbDrone.Core/RootFolders/RootFolder.cs
+++ b/src/NzbDrone.Core/RootFolders/RootFolder.cs
@@ -12,6 +12,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 DefaultTags { get; set; }
public bool IsCalibreLibrary { get; set; }
public CalibreSettings CalibreSettings { get; set; }
diff --git a/src/Readarr.Api.V1/Author/AuthorController.cs b/src/Readarr.Api.V1/Author/AuthorController.cs
index 55ca08bcd..5eaa6e98f 100644
--- a/src/Readarr.Api.V1/Author/AuthorController.cs
+++ b/src/Readarr.Api.V1/Author/AuthorController.cs
@@ -18,7 +18,6 @@ using NzbDrone.Core.Validation;
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;
diff --git a/src/Readarr.Api.V1/Author/AuthorEditorController.cs b/src/Readarr.Api.V1/Author/AuthorEditorController.cs
index febaf64ad..84dab433a 100644
--- a/src/Readarr.Api.V1/Author/AuthorEditorController.cs
+++ b/src/Readarr.Api.V1/Author/AuthorEditorController.cs
@@ -34,6 +34,11 @@ namespace Readarr.Api.V1.Author
author.Monitored = resource.Monitored.Value;
}
+ if (resource.MonitorNewItems.HasValue)
+ {
+ author.MonitorNewItems = resource.MonitorNewItems.Value;
+ }
+
if (resource.QualityProfileId.HasValue)
{
author.QualityProfileId = resource.QualityProfileId.Value;
diff --git a/src/Readarr.Api.V1/Author/AuthorEditorResource.cs b/src/Readarr.Api.V1/Author/AuthorEditorResource.cs
index c599a9714..9834792b4 100644
--- a/src/Readarr.Api.V1/Author/AuthorEditorResource.cs
+++ b/src/Readarr.Api.V1/Author/AuthorEditorResource.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using NzbDrone.Core.Books;
namespace Readarr.Api.V1.Author
{
@@ -6,6 +7,7 @@ namespace Readarr.Api.V1.Author
{
public List 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; }
diff --git a/src/Readarr.Api.V1/Author/AuthorResource.cs b/src/Readarr.Api.V1/Author/AuthorResource.cs
index be4bf0035..f6751b191 100644
--- a/src/Readarr.Api.V1/Author/AuthorResource.cs
+++ b/src/Readarr.Api.V1/Author/AuthorResource.cs
@@ -5,7 +5,6 @@ using Newtonsoft.Json;
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 @@ namespace Readarr.Api.V1.Author
//Editing Only
public bool Monitored { get; set; }
+ public NewItemMonitorTypes MonitorNewItems { get; set; }
public string RootFolderPath { get; set; }
public List Genres { get; set; }
@@ -91,6 +91,7 @@ namespace Readarr.Api.V1.Author
Links = model.Metadata.Value.Links,
Monitored = model.Monitored,
+ MonitorNewItems = model.MonitorNewItems,
CleanName = model.CleanName,
ForeignAuthorId = model.Metadata.Value.ForeignAuthorId,
@@ -141,6 +142,7 @@ namespace Readarr.Api.V1.Author
MetadataProfileId = resource.MetadataProfileId,
Monitored = resource.Monitored,
+ MonitorNewItems = resource.MonitorNewItems,
CleanName = resource.CleanName,
RootFolderPath = resource.RootFolderPath,
diff --git a/src/Readarr.Api.V1/BookShelf/BookshelfController.cs b/src/Readarr.Api.V1/BookShelf/BookshelfController.cs
index b38f9b382..9f040190b 100644
--- a/src/Readarr.Api.V1/BookShelf/BookshelfController.cs
+++ b/src/Readarr.Api.V1/BookShelf/BookshelfController.cs
@@ -18,7 +18,7 @@ namespace Readarr.Api.V1.Bookshelf
}
[HttpPost]
- public ActionResult