New: Rework List sync interval logic

* New: Rework List sync interval logic

(cherry picked from commit c522cd120d08757e7e43c2348be4d7f05a254fac)

* Minor alignment with Sonarr

* Remove ListUpdateInterval

---------

Co-authored-by: Qstick <qstick@gmail.com>
This commit is contained in:
Bogdan 2023-07-04 19:17:28 +03:00 committed by GitHub
parent 148ee5983d
commit bd1844030d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 174 additions and 101 deletions

View File

@ -3,3 +3,9 @@
margin-right: auto; margin-right: auto;
} }
.message {
composes: alert from '~Components/Alert.css';
margin-bottom: 30px;
}

View File

@ -2,6 +2,7 @@
// Please do not change this file! // Please do not change this file!
interface CssExports { interface CssExports {
'deleteButton': string; 'deleteButton': string;
'message': string;
} }
export const cssExports: CssExports; export const cssExports: CssExports;
export default cssExports; export default cssExports;

View File

@ -14,6 +14,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter'; import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader'; import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props'; import { inputTypes, kinds } from 'Helpers/Props';
import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './EditImportListModalContent.css'; import styles from './EditImportListModalContent.css';
@ -42,6 +43,7 @@ function EditImportListModalContent(props) {
name, name,
enabled, enabled,
enableAuto, enableAuto,
minRefreshInterval,
monitor, monitor,
minimumAvailability, minimumAvailability,
qualityProfileId, qualityProfileId,
@ -85,6 +87,14 @@ function EditImportListModalContent(props) {
{message.value.message} {message.value.message}
</Alert> </Alert>
} }
<Alert
kind={kinds.INFO}
className={styles.message}
>
{translate('ListWillRefreshEveryInterp', [formatShortTimeSpan(minRefreshInterval.value)])}
</Alert>
<FormGroup> <FormGroup>
<FormLabel>{translate('Name')}</FormLabel> <FormLabel>{translate('Name')}</FormLabel>

View File

@ -4,6 +4,7 @@ import Card from 'Components/Card';
import Label from 'Components/Label'; import Label from 'Components/Label';
import ConfirmModal from 'Components/Modal/ConfirmModal'; import ConfirmModal from 'Components/Modal/ConfirmModal';
import { kinds } from 'Helpers/Props'; import { kinds } from 'Helpers/Props';
import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import EditImportListModalConnector from './EditImportListModalConnector'; import EditImportListModalConnector from './EditImportListModalConnector';
import styles from './ImportList.css'; import styles from './ImportList.css';
@ -56,7 +57,8 @@ class ImportList extends Component {
id, id,
name, name,
enabled, enabled,
enableAuto enableAuto,
minRefreshInterval
} = this.props; } = this.props;
return ( return (
@ -96,6 +98,12 @@ class ImportList extends Component {
} }
</div> </div>
<div className={styles.enabled}>
<Label kind={kinds.INFO} title='List Refresh Interval'>
{`${translate('Refresh')}: ${formatShortTimeSpan(minRefreshInterval)}`}
</Label>
</div>
<EditImportListModalConnector <EditImportListModalConnector
id={id} id={id}
isOpen={this.state.isEditImportListModalOpen} isOpen={this.state.isEditImportListModalOpen}
@ -122,6 +130,7 @@ ImportList.propTypes = {
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
enabled: PropTypes.bool.isRequired, enabled: PropTypes.bool.isRequired,
enableAuto: PropTypes.bool.isRequired, enableAuto: PropTypes.bool.isRequired,
minRefreshInterval: PropTypes.string.isRequired,
onConfirmDeleteImportList: PropTypes.func.isRequired onConfirmDeleteImportList: PropTypes.func.isRequired
}; };

View File

@ -46,23 +46,6 @@ function ImportListOptions(props) {
{ {
hasSettings && !isFetching && !error && hasSettings && !isFetching && !error &&
<Form> <Form>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>{translate('ListUpdateInterval')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="importListSyncInterval"
min={6}
unit="hours"
helpText={translate('ImportListSyncIntervalHelpText')}
onChange={onInputChange}
{...settings.importListSyncInterval}
/>
</FormGroup>
<FormGroup <FormGroup
advancedSettings={advancedSettings} advancedSettings={advancedSettings}
isAdvanced={true} isAdvanced={true}

View File

@ -0,0 +1,25 @@
import moment from 'moment';
function formatShortTimeSpan(timeSpan) {
if (!timeSpan) {
return '';
}
const duration = moment.duration(timeSpan);
const hours = Math.floor(duration.asHours());
const minutes = Math.floor(duration.asMinutes());
const seconds = Math.floor(duration.asSeconds());
if (hours > 0) {
return `${hours} hour(s)`;
}
if (minutes > 0) {
return `${minutes} minute(s)`;
}
return `${seconds} second(s)`;
}
export default formatShortTimeSpan;

View File

@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.ImportListTests
_blockedLists = new List<ImportListStatus>(); _blockedLists = new List<ImportListStatus>();
Mocker.GetMock<IImportListFactory>() Mocker.GetMock<IImportListFactory>()
.Setup(v => v.Enabled()) .Setup(v => v.Enabled(It.IsAny<bool>()))
.Returns(_importLists); .Returns(_importLists);
Mocker.GetMock<IImportListStatusService>() Mocker.GetMock<IImportListStatusService>()
@ -183,7 +183,7 @@ namespace NzbDrone.Core.Test.ImportListTests
var listResult = Subject.Fetch(); var listResult = Subject.Fetch();
listResult.AnyFailure.Should().BeFalse(); listResult.AnyFailure.Should().BeFalse();
listResult.Movies.Count.Should().Be(10); listResult.Movies.Count.Should().Be(5);
} }
} }
} }

View File

@ -72,7 +72,7 @@ namespace NzbDrone.Core.Test.ImportList
}; };
Mocker.GetMock<IImportListFactory>() Mocker.GetMock<IImportListFactory>()
.Setup(v => v.Enabled()) .Setup(v => v.Enabled(It.IsAny<bool>()))
.Returns(_importLists); .Returns(_importLists);
Mocker.GetMock<IImportExclusionsService>() Mocker.GetMock<IImportExclusionsService>()
@ -347,7 +347,7 @@ namespace NzbDrone.Core.Test.ImportList
} }
[Test] [Test]
public void should_not_add_duplicate_movies_from_seperate_lists() public void should_not_add_duplicate_movies_from_separate_lists()
{ {
_list2Movies.ForEach(m => m.ListId = 2); _list2Movies.ForEach(m => m.ListId = 2);
_importListFetch.Movies.ForEach(m => m.ListId = 1); _importListFetch.Movies.ForEach(m => m.ListId = 1);
@ -384,7 +384,7 @@ namespace NzbDrone.Core.Test.ImportList
Subject.Execute(_commandAll); Subject.Execute(_commandAll);
Mocker.GetMock<IAddMovieService>() Mocker.GetMock<IAddMovieService>()
.Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 7 && !s.Any(m => m.TmdbId == _existingMovies[0].TmdbId)), true), Times.Once()); .Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 7 && s.All(m => m.TmdbId != _existingMovies[0].TmdbId)), true), Times.Once());
} }
[Test] [Test]
@ -406,7 +406,7 @@ namespace NzbDrone.Core.Test.ImportList
Subject.Execute(_commandAll); Subject.Execute(_commandAll);
Mocker.GetMock<IAddMovieService>() Mocker.GetMock<IAddMovieService>()
.Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 7 && !s.Any(m => m.TmdbId == _existingMovies[0].TmdbId)), true), Times.Once()); .Verify(v => v.AddMovies(It.Is<List<Movie>>(s => s.Count == 7 && s.All(m => m.TmdbId != _existingMovies[0].TmdbId)), true), Times.Once());
} }
} }
} }

View File

@ -116,13 +116,6 @@ namespace NzbDrone.Core.Configuration
set { SetValue("AvailabilityDelay", value); } set { SetValue("AvailabilityDelay", value); }
} }
public int ImportListSyncInterval
{
get { return GetValueInt("ImportListSyncInterval", 24); }
set { SetValue("ImportListSyncInterval", value); }
}
public string ListSyncLevel public string ListSyncLevel
{ {
get { return GetValue("ListSyncLevel", "disabled"); } get { return GetValue("ListSyncLevel", "disabled"); }

View File

@ -60,7 +60,6 @@ namespace NzbDrone.Core.Configuration
bool AllowHardcodedSubs { get; set; } bool AllowHardcodedSubs { get; set; }
string WhitelistedHardcodedSubs { get; set; } string WhitelistedHardcodedSubs { get; set; }
int ImportListSyncInterval { get; set; }
string ListSyncLevel { get; set; } string ListSyncLevel { get; set; }
string ImportExclusions { get; set; } string ImportExclusions { get; set; }

View File

@ -0,0 +1,18 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(224)]
public class list_sync_time : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Delete.Column("LastSyncListInfo").FromTable("ImportListStatus");
Alter.Table("ImportListStatus").AddColumn("LastInfoSync").AsDateTimeOffset().Nullable();
Delete.FromTable("Config").Row(new { Key = "importlistsyncinterval" });
}
}
}

View File

@ -80,6 +80,7 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<ImportListDefinition>("ImportLists").RegisterModel() Mapper.Entity<ImportListDefinition>("ImportLists").RegisterModel()
.Ignore(x => x.ImplementationName) .Ignore(x => x.ImplementationName)
.Ignore(i => i.ListType) .Ignore(i => i.ListType)
.Ignore(i => i.MinRefreshInterval)
.Ignore(i => i.Enable); .Ignore(i => i.Enable);
Mapper.Entity<NotificationDefinition>("Notifications").RegisterModel() Mapper.Entity<NotificationDefinition>("Notifications").RegisterModel()

View File

@ -1,3 +1,4 @@
using System;
using NLog; using NLog;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
@ -10,6 +11,7 @@ namespace NzbDrone.Core.ImportLists.CouchPotato
public override string Name => "CouchPotato"; public override string Name => "CouchPotato";
public override ImportListType ListType => ImportListType.Program; public override ImportListType ListType => ImportListType.Program;
public override TimeSpan MinRefreshInterval => TimeSpan.FromMinutes(30);
public override bool Enabled => true; public override bool Enabled => true;
public override bool EnableAuto => false; public override bool EnableAuto => false;

View File

@ -66,6 +66,15 @@ namespace NzbDrone.Core.ImportLists
_logger.ProgressInfo("Syncing Movies for List: {0}", importList.Name); _logger.ProgressInfo("Syncing Movies for List: {0}", importList.Name);
var importListLocal = importList; var importListLocal = importList;
var importListStatus = _importListStatusService.GetLastSyncListInfo(importListLocal.Definition.Id);
if (importListStatus.HasValue && DateTime.UtcNow < importListStatus + importListLocal.MinRefreshInterval)
{
_logger.Trace("Skipping refresh of Import List {0} due to minimum refresh inverval", importListLocal.Definition.Name);
continue;
}
var blockedLists = _importListStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v); var blockedLists = _importListStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
if (blockedLists.TryGetValue(importList.Definition.Id, out var blockedListStatus)) if (blockedLists.TryGetValue(importList.Definition.Id, out var blockedListStatus))
@ -88,7 +97,7 @@ namespace NzbDrone.Core.ImportLists
if (!importListReports.AnyFailure) if (!importListReports.AnyFailure)
{ {
var alreadyMapped = result.Movies.Where(x => importListReports.Movies.Any(r => r.TmdbId == x.TmdbId)); var alreadyMapped = result.Movies.Where(x => importListReports.Movies.Any(r => r.TmdbId == x.TmdbId));
var listMovies = MapMovieReports(importListReports.Movies.Where(x => !result.Movies.Any(r => r.TmdbId == x.TmdbId)).ToList()).Where(x => x.TmdbId > 0).ToList(); var listMovies = MapMovieReports(importListReports.Movies.Where(x => result.Movies.All(r => r.TmdbId != x.TmdbId)).ToList()).Where(x => x.TmdbId > 0).ToList();
listMovies.AddRange(alreadyMapped); listMovies.AddRange(alreadyMapped);
listMovies = listMovies.DistinctBy(x => x.TmdbId).ToList(); listMovies = listMovies.DistinctBy(x => x.TmdbId).ToList();
@ -99,6 +108,8 @@ namespace NzbDrone.Core.ImportLists
} }
result.AnyFailure |= importListReports.AnyFailure; result.AnyFailure |= importListReports.AnyFailure;
_importListStatusService.UpdateListSyncStatus(importList.Definition.Id);
} }
} }
catch (Exception e) catch (Exception e)
@ -112,6 +123,8 @@ namespace NzbDrone.Core.ImportLists
Task.WaitAll(taskList.ToArray()); Task.WaitAll(taskList.ToArray());
result.Movies = result.Movies.DistinctBy(r => new { r.TmdbId, r.ImdbId, r.Title }).ToList();
_logger.Debug("Found {0} reports for all lists", result.Movies.Count); _logger.Debug("Found {0} reports for all lists", result.Movies.Count);
return result; return result;
@ -151,6 +164,8 @@ namespace NzbDrone.Core.ImportLists
} }
result.AnyFailure |= importListReports.AnyFailure; result.AnyFailure |= importListReports.AnyFailure;
_importListStatusService.UpdateListSyncStatus(importList.Definition.Id);
} }
} }
catch (Exception e) catch (Exception e)
@ -158,6 +173,8 @@ namespace NzbDrone.Core.ImportLists
_logger.Error(e, "Error during Import List Sync for list {0}", importList.Name); _logger.Error(e, "Error during Import List Sync for list {0}", importList.Name);
} }
result.Movies = result.Movies.DistinctBy(r => new { r.TmdbId, r.ImdbId, r.Title }).ToList();
_logger.Debug("Found {0} reports for list {1}", result.Movies.Count, importList.Name); _logger.Debug("Found {0} reports for list {1}", result.Movies.Count, importList.Name);
return result; return result;
@ -166,9 +183,9 @@ namespace NzbDrone.Core.ImportLists
private List<ImportListMovie> MapMovieReports(List<ImportListMovie> reports) private List<ImportListMovie> MapMovieReports(List<ImportListMovie> reports)
{ {
var mappedMovies = reports.Select(m => _movieSearch.MapMovieToTmdbMovie(new MovieMetadata { Title = m.Title, TmdbId = m.TmdbId, ImdbId = m.ImdbId, Year = m.Year })) var mappedMovies = reports.Select(m => _movieSearch.MapMovieToTmdbMovie(new MovieMetadata { Title = m.Title, TmdbId = m.TmdbId, ImdbId = m.ImdbId, Year = m.Year }))
.Where(x => x != null) .Where(x => x != null)
.DistinctBy(x => x.TmdbId) .DistinctBy(x => x.TmdbId)
.ToList(); .ToList();
_movieMetadataService.UpsertMany(mappedMovies); _movieMetadataService.UpsertMany(mappedMovies);

View File

@ -1,3 +1,4 @@
using System;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.ImportLists namespace NzbDrone.Core.ImportLists
@ -8,6 +9,7 @@ namespace NzbDrone.Core.ImportLists
bool EnableAuto { get; } bool EnableAuto { get; }
ImportListType ListType { get; } ImportListType ListType { get; }
TimeSpan MinRefreshInterval { get; }
ImportListFetchResult Fetch(); ImportListFetchResult Fetch();
} }
} }

View File

@ -32,6 +32,7 @@ namespace NzbDrone.Core.ImportLists
public abstract string Name { get; } public abstract string Name { get; }
public abstract ImportListType ListType { get; } public abstract ImportListType ListType { get; }
public abstract TimeSpan MinRefreshInterval { get; }
public abstract bool Enabled { get; } public abstract bool Enabled { get; }
public abstract bool EnableAuto { get; } public abstract bool EnableAuto { get; }

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
@ -21,5 +22,6 @@ namespace NzbDrone.Core.ImportLists
public override bool Enable => Enabled; public override bool Enable => Enabled;
public ImportListType ListType { get; set; } public ImportListType ListType { get; set; }
public TimeSpan MinRefreshInterval { get; set; }
} }
} }

View File

@ -9,23 +9,24 @@ namespace NzbDrone.Core.ImportLists
{ {
public interface IImportListFactory : IProviderFactory<IImportList, ImportListDefinition> public interface IImportListFactory : IProviderFactory<IImportList, ImportListDefinition>
{ {
List<IImportList> Enabled(); List<IImportList> Enabled(bool filterBlockedImportLists = true);
List<IImportList> Discoverable(); List<IImportList> Discoverable();
} }
public class ImportListFactory : ProviderFactory<IImportList, ImportListDefinition>, IImportListFactory public class ImportListFactory : ProviderFactory<IImportList, ImportListDefinition>, IImportListFactory
{ {
private readonly IImportListRepository _providerRepository; private readonly IImportListStatusService _importListStatusService;
private readonly Logger _logger; private readonly Logger _logger;
public ImportListFactory(IImportListRepository providerRepository, public ImportListFactory(IImportListStatusService importListStatusService,
IImportListRepository providerRepository,
IEnumerable<IImportList> providers, IEnumerable<IImportList> providers,
IServiceProvider container, IServiceProvider container,
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
Logger logger) Logger logger)
: base(providerRepository, providers, container, eventAggregator, logger) : base(providerRepository, providers, container, eventAggregator, logger)
{ {
_providerRepository = providerRepository; _importListStatusService = importListStatusService;
_logger = logger; _logger = logger;
} }
@ -39,18 +40,42 @@ namespace NzbDrone.Core.ImportLists
base.SetProviderCharacteristics(provider, definition); base.SetProviderCharacteristics(provider, definition);
definition.ListType = provider.ListType; definition.ListType = provider.ListType;
definition.MinRefreshInterval = provider.MinRefreshInterval;
} }
public List<IImportList> Enabled() public List<IImportList> Enabled(bool filterBlockedImportLists = true)
{ {
var enabledImporters = GetAvailableProviders().Where(n => ((ImportListDefinition)n.Definition).Enabled); var enabledImportLists = GetAvailableProviders().Where(n => ((ImportListDefinition)n.Definition).Enabled);
return enabledImporters.ToList();
if (filterBlockedImportLists)
{
return FilterBlockedImportLists(enabledImportLists).ToList();
}
return enabledImportLists.ToList();
} }
public List<IImportList> Discoverable() public List<IImportList> Discoverable()
{ {
var enabledImporters = GetAvailableProviders().Where(n => (n.GetType() == typeof(RadarrList.RadarrListImport) || n.GetType() == typeof(TMDb.Popular.TMDbPopularImport))); var enabledImportLists = GetAvailableProviders().Where(n => n.GetType() == typeof(RadarrList.RadarrListImport) || n.GetType() == typeof(TMDb.Popular.TMDbPopularImport));
return enabledImporters.ToList();
return enabledImportLists.ToList();
}
private IEnumerable<IImportList> FilterBlockedImportLists(IEnumerable<IImportList> importLists)
{
var blockedImportLists = _importListStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
foreach (var importList in importLists)
{
if (blockedImportLists.TryGetValue(importList.Definition.Id, out var blockedImportListStatus))
{
_logger.Debug("Temporarily ignoring import list {0} till {1} due to recent failures.", importList.Definition.Name, blockedImportListStatus.DisabledTill.Value.ToLocalTime());
continue;
}
yield return importList;
}
} }
} }
} }

View File

@ -1,10 +1,10 @@
using NzbDrone.Core.Movies; using System;
using NzbDrone.Core.ThingiProvider.Status; using NzbDrone.Core.ThingiProvider.Status;
namespace NzbDrone.Core.ImportLists namespace NzbDrone.Core.ImportLists
{ {
public class ImportListStatus : ProviderStatusBase public class ImportListStatus : ProviderStatusBase
{ {
public Movie LastSyncListInfo { get; set; } public DateTime? LastInfoSync { get; set; }
} }
} }

View File

@ -1,16 +1,16 @@
using System;
using NLog; using NLog;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies;
using NzbDrone.Core.ThingiProvider.Status; using NzbDrone.Core.ThingiProvider.Status;
namespace NzbDrone.Core.ImportLists namespace NzbDrone.Core.ImportLists
{ {
public interface IImportListStatusService : IProviderStatusServiceBase<ImportListStatus> public interface IImportListStatusService : IProviderStatusServiceBase<ImportListStatus>
{ {
Movie GetLastSyncListInfo(int importListId); DateTime? GetLastSyncListInfo(int importListId);
void UpdateListSyncStatus(int importListId, Movie listItemInfo); void UpdateListSyncStatus(int importListId);
} }
public class ImportListStatusService : ProviderStatusServiceBase<IImportList, ImportListStatus>, IImportListStatusService public class ImportListStatusService : ProviderStatusServiceBase<IImportList, ImportListStatus>, IImportListStatusService
@ -20,18 +20,18 @@ namespace NzbDrone.Core.ImportLists
{ {
} }
public Movie GetLastSyncListInfo(int importListId) public DateTime? GetLastSyncListInfo(int importListId)
{ {
return GetProviderStatus(importListId).LastSyncListInfo; return GetProviderStatus(importListId).LastInfoSync;
} }
public void UpdateListSyncStatus(int importListId, Movie listItemInfo) public void UpdateListSyncStatus(int importListId)
{ {
lock (_syncRoot) lock (_syncRoot)
{ {
var status = GetProviderStatus(importListId); var status = GetProviderStatus(importListId);
status.LastSyncListInfo = listItemInfo; status.LastInfoSync = DateTime.UtcNow;
_providerStatusRepository.Upsert(status); _providerStatusRepository.Upsert(status);
} }

View File

@ -15,6 +15,7 @@ namespace NzbDrone.Core.ImportLists.Plex
{ {
public readonly IPlexTvService _plexTvService; public readonly IPlexTvService _plexTvService;
public override ImportListType ListType => ImportListType.Plex; public override ImportListType ListType => ImportListType.Plex;
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(6);
public PlexImport(IPlexTvService plexTvService, public PlexImport(IPlexTvService plexTvService,
IHttpClient httpClient, IHttpClient httpClient,

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using NLog; using NLog;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
@ -12,6 +13,7 @@ namespace NzbDrone.Core.ImportLists.RSSImport
public override string Name => "RSS List"; public override string Name => "RSS List";
public override ImportListType ListType => ImportListType.Advanced; public override ImportListType ListType => ImportListType.Advanced;
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12);
public override bool Enabled => true; public override bool Enabled => true;
public override bool EnableAuto => false; public override bool EnableAuto => false;

View File

@ -19,6 +19,7 @@ namespace NzbDrone.Core.ImportLists.Radarr
public override bool EnableAuto => false; public override bool EnableAuto => false;
public override ImportListType ListType => ImportListType.Program; public override ImportListType ListType => ImportListType.Program;
public override TimeSpan MinRefreshInterval => TimeSpan.FromMinutes(15);
public RadarrImport(IRadarrV3Proxy radarrV3Proxy, public RadarrImport(IRadarrV3Proxy radarrV3Proxy,
IImportListStatusService importListStatusService, IImportListStatusService importListStatusService,

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using NLog; using NLog;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
@ -12,6 +13,7 @@ namespace NzbDrone.Core.ImportLists.RadarrList
public override string Name => "Custom Lists"; public override string Name => "Custom Lists";
public override ImportListType ListType => ImportListType.Advanced; public override ImportListType ListType => ImportListType.Advanced;
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12);
public override bool Enabled => true; public override bool Enabled => true;
public override bool EnableAuto => false; public override bool EnableAuto => false;

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using NLog; using NLog;
using NzbDrone.Common.Cloud; using NzbDrone.Common.Cloud;
@ -15,6 +16,7 @@ namespace NzbDrone.Core.ImportLists.RadarrList2.IMDbList
public override string Name => "IMDb Lists"; public override string Name => "IMDb Lists";
public override ImportListType ListType => ImportListType.Other; public override ImportListType ListType => ImportListType.Other;
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12);
public override bool Enabled => true; public override bool Enabled => true;
public override bool EnableAuto => false; public override bool EnableAuto => false;

View File

@ -1,3 +1,4 @@
using System;
using NLog; using NLog;
using NzbDrone.Common.Cloud; using NzbDrone.Common.Cloud;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
@ -13,6 +14,8 @@ namespace NzbDrone.Core.ImportLists.RadarrList2.StevenLu
public override string Name => "StevenLu List"; public override string Name => "StevenLu List";
public override ImportListType ListType => ImportListType.Other; public override ImportListType ListType => ImportListType.Other;
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12);
public override bool Enabled => true; public override bool Enabled => true;
public override bool EnableAuto => false; public override bool EnableAuto => false;

View File

@ -1,3 +1,4 @@
using System;
using NLog; using NLog;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
@ -9,6 +10,7 @@ namespace NzbDrone.Core.ImportLists.Rss.Plex
{ {
public override string Name => "Plex Watchlist RSS"; public override string Name => "Plex Watchlist RSS";
public override ImportListType ListType => ImportListType.Plex; public override ImportListType ListType => ImportListType.Plex;
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(6);
public PlexRssImport(IHttpClient httpClient, public PlexRssImport(IHttpClient httpClient,
IImportListStatusService importListStatusService, IImportListStatusService importListStatusService,

View File

@ -1,3 +1,4 @@
using System;
using NLog; using NLog;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
@ -10,6 +11,7 @@ namespace NzbDrone.Core.ImportLists.StevenLu
public override string Name => "StevenLu Custom"; public override string Name => "StevenLu Custom";
public override ImportListType ListType => ImportListType.Advanced; public override ImportListType ListType => ImportListType.Advanced;
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(24);
public override bool Enabled => true; public override bool Enabled => true;
public override bool EnableAuto => false; public override bool EnableAuto => false;

View File

@ -1,3 +1,4 @@
using System;
using NLog; using NLog;
using NzbDrone.Common.Cloud; using NzbDrone.Common.Cloud;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
@ -11,6 +12,7 @@ namespace NzbDrone.Core.ImportLists.TMDb
where TSettings : TMDbSettingsBase<TSettings>, new() where TSettings : TMDbSettingsBase<TSettings>, new()
{ {
public override ImportListType ListType => ImportListType.TMDB; public override ImportListType ListType => ImportListType.TMDB;
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12);
public readonly ISearchForNewMovie _skyhookProxy; public readonly ISearchForNewMovie _skyhookProxy;
public readonly IHttpRequestBuilderFactory _requestBuilder; public readonly IHttpRequestBuilderFactory _requestBuilder;

View File

@ -15,6 +15,7 @@ namespace NzbDrone.Core.ImportLists.Trakt
public ITraktProxy _traktProxy; public ITraktProxy _traktProxy;
private readonly IImportListRepository _importListRepository; private readonly IImportListRepository _importListRepository;
public override ImportListType ListType => ImportListType.Trakt; public override ImportListType ListType => ImportListType.Trakt;
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12);
protected TraktImportBase(IImportListRepository importListRepository, protected TraktImportBase(IImportListRepository importListRepository,
ITraktProxy traktProxy, ITraktProxy traktProxy,

View File

@ -121,7 +121,7 @@ namespace NzbDrone.Core.Jobs
new ScheduledTask new ScheduledTask
{ {
Interval = GetImportListSyncInterval(), Interval = 5,
TypeName = typeof(ImportListSyncCommand).FullName TypeName = typeof(ImportListSyncCommand).FullName
}, },
@ -210,14 +210,6 @@ namespace NzbDrone.Core.Jobs
return interval; return interval;
} }
private int GetImportListSyncInterval()
{
// Enforce 6 hour min on list sync
var interval = Math.Max(_configService.ImportListSyncInterval, 6);
return interval * 60;
}
public void Handle(CommandExecutedEvent message) public void Handle(CommandExecutedEvent message)
{ {
var scheduledTask = _scheduledTaskRepository.All().SingleOrDefault(c => c.TypeName == message.Command.Body.GetType().FullName); var scheduledTask = _scheduledTaskRepository.All().SingleOrDefault(c => c.TypeName == message.Command.Body.GetType().FullName);
@ -239,19 +231,15 @@ namespace NzbDrone.Core.Jobs
var rss = _scheduledTaskRepository.GetDefinition(typeof(RssSyncCommand)); var rss = _scheduledTaskRepository.GetDefinition(typeof(RssSyncCommand));
rss.Interval = GetRssSyncInterval(); rss.Interval = GetRssSyncInterval();
var importList = _scheduledTaskRepository.GetDefinition(typeof(ImportListSyncCommand));
importList.Interval = GetImportListSyncInterval();
var backup = _scheduledTaskRepository.GetDefinition(typeof(BackupCommand)); var backup = _scheduledTaskRepository.GetDefinition(typeof(BackupCommand));
backup.Interval = GetBackupInterval(); backup.Interval = GetBackupInterval();
var refreshMonitoredDownloads = _scheduledTaskRepository.GetDefinition(typeof(RefreshMonitoredDownloadsCommand)); var refreshMonitoredDownloads = _scheduledTaskRepository.GetDefinition(typeof(RefreshMonitoredDownloadsCommand));
refreshMonitoredDownloads.Interval = GetRefreshMonitoredInterval(); refreshMonitoredDownloads.Interval = GetRefreshMonitoredInterval();
_scheduledTaskRepository.UpdateMany(new List<ScheduledTask> { rss, importList, refreshMonitoredDownloads, backup }); _scheduledTaskRepository.UpdateMany(new List<ScheduledTask> { rss, refreshMonitoredDownloads, backup });
_cache.Find(rss.TypeName).Interval = rss.Interval; _cache.Find(rss.TypeName).Interval = rss.Interval;
_cache.Find(importList.TypeName).Interval = importList.Interval;
_cache.Find(backup.TypeName).Interval = backup.Interval; _cache.Find(backup.TypeName).Interval = backup.Interval;
_cache.Find(refreshMonitoredDownloads.TypeName).Interval = refreshMonitoredDownloads.Interval; _cache.Find(refreshMonitoredDownloads.TypeName).Interval = refreshMonitoredDownloads.Interval;
} }

View File

@ -416,7 +416,6 @@
"ImportListMultipleMissingRoots": "Multiple root folders are missing for import lists: {0}", "ImportListMultipleMissingRoots": "Multiple root folders are missing for import lists: {0}",
"ImportListStatusCheckAllClientMessage": "All lists are unavailable due to failures", "ImportListStatusCheckAllClientMessage": "All lists are unavailable due to failures",
"ImportListStatusCheckSingleClientMessage": "Lists unavailable due to failures: {0}", "ImportListStatusCheckSingleClientMessage": "Lists unavailable due to failures: {0}",
"ImportListSyncIntervalHelpText": "How often Radarr syncs with your lists. Minimum value of 6 hours",
"ImportMechanismHealthCheckMessage": "Enable Completed Download Handling", "ImportMechanismHealthCheckMessage": "Enable Completed Download Handling",
"ImportMovies": "Import Movies", "ImportMovies": "Import Movies",
"ImportNotForDownloads": "Do not use for importing downloads from your download client, this is only for existing organized libraries, not unsorted files.", "ImportNotForDownloads": "Do not use for importing downloads from your download client, this is only for existing organized libraries, not unsorted files.",
@ -484,11 +483,12 @@
"Links": "Links", "Links": "Links",
"List": "List", "List": "List",
"ListExclusions": "List Exclusions", "ListExclusions": "List Exclusions",
"ListRefreshInterval": "List Refresh Interval",
"ListSettings": "List Settings", "ListSettings": "List Settings",
"ListSyncLevelHelpText": "Movies in library will be handled based on your selection if they fall off or do not appear on your list(s)", "ListSyncLevelHelpText": "Movies in library will be handled based on your selection if they fall off or do not appear on your list(s)",
"ListSyncLevelHelpTextWarning": "Movie files will be permanently deleted, this can result in wiping your library if your lists are empty", "ListSyncLevelHelpTextWarning": "Movie files will be permanently deleted, this can result in wiping your library if your lists are empty",
"ListTagsHelpText": "Tags list items will be added with", "ListTagsHelpText": "Tags list items will be added with",
"ListUpdateInterval": "List Update Interval", "ListWillRefreshEveryInterp": "List will refresh every {0}",
"Lists": "Lists", "Lists": "Lists",
"ListsSettingsSummary": "Import Lists, list exclusions", "ListsSettingsSummary": "Import Lists, list exclusions",
"Loading": "Loading", "Loading": "Loading",

View File

@ -1,6 +1,5 @@
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using Radarr.Http; using Radarr.Http;
using Radarr.Http.Validation;
namespace Radarr.Api.V3.Config namespace Radarr.Api.V3.Config
{ {
@ -11,8 +10,6 @@ namespace Radarr.Api.V3.Config
public ImportListConfigController(IConfigService configService) public ImportListConfigController(IConfigService configService)
: base(configService) : base(configService)
{ {
SharedValidator.RuleFor(c => c.ImportListSyncInterval)
.IsValidImportListSyncInterval();
} }
protected override ImportListConfigResource ToResource(IConfigService model) protected override ImportListConfigResource ToResource(IConfigService model)

View File

@ -5,7 +5,6 @@ namespace Radarr.Api.V3.Config
{ {
public class ImportListConfigResource : RestResource public class ImportListConfigResource : RestResource
{ {
public int ImportListSyncInterval { get; set; }
public string ListSyncLevel { get; set; } public string ListSyncLevel { get; set; }
public string ImportExclusions { get; set; } public string ImportExclusions { get; set; }
} }
@ -16,7 +15,6 @@ namespace Radarr.Api.V3.Config
{ {
return new ImportListConfigResource return new ImportListConfigResource
{ {
ImportListSyncInterval = model.ImportListSyncInterval,
ListSyncLevel = model.ListSyncLevel, ListSyncLevel = model.ListSyncLevel,
ImportExclusions = model.ImportExclusions ImportExclusions = model.ImportExclusions
}; };

View File

@ -1,3 +1,4 @@
using System;
using NzbDrone.Core.ImportLists; using NzbDrone.Core.ImportLists;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
@ -14,6 +15,7 @@ namespace Radarr.Api.V3.ImportLists
public MovieStatusType MinimumAvailability { get; set; } public MovieStatusType MinimumAvailability { get; set; }
public ImportListType ListType { get; set; } public ImportListType ListType { get; set; }
public int ListOrder { get; set; } public int ListOrder { get; set; }
public TimeSpan MinRefreshInterval { get; set; }
} }
public class ImportListResourceMapper : ProviderResourceMapper<ImportListResource, ImportListDefinition> public class ImportListResourceMapper : ProviderResourceMapper<ImportListResource, ImportListDefinition>
@ -36,6 +38,7 @@ namespace Radarr.Api.V3.ImportLists
resource.MinimumAvailability = definition.MinimumAvailability; resource.MinimumAvailability = definition.MinimumAvailability;
resource.ListType = definition.ListType; resource.ListType = definition.ListType;
resource.ListOrder = (int)definition.ListType; resource.ListOrder = (int)definition.ListType;
resource.MinRefreshInterval = definition.MinRefreshInterval;
return resource; return resource;
} }
@ -57,6 +60,7 @@ namespace Radarr.Api.V3.ImportLists
definition.ProfileId = resource.QualityProfileId; definition.ProfileId = resource.QualityProfileId;
definition.MinimumAvailability = resource.MinimumAvailability; definition.MinimumAvailability = resource.MinimumAvailability;
definition.ListType = resource.ListType; definition.ListType = resource.ListType;
definition.MinRefreshInterval = resource.MinRefreshInterval;
return definition; return definition;
} }

View File

@ -1,21 +0,0 @@
using FluentValidation.Validators;
namespace Radarr.Http.Validation
{
public class ImportListSyncIntervalValidator : PropertyValidator
{
protected override string GetDefaultMessageTemplate() => "Must be greater than 6 hours";
protected override bool IsValid(PropertyValidatorContext context)
{
if (context.PropertyValue == null)
{
return true;
}
var value = (int)context.PropertyValue;
return value >= 6;
}
}
}

View File

@ -36,10 +36,5 @@ namespace Radarr.Http.Validation
{ {
return ruleBuilder.SetValidator(new RssSyncIntervalValidator()); return ruleBuilder.SetValidator(new RssSyncIntervalValidator());
} }
public static IRuleBuilderOptions<T, int> IsValidImportListSyncInterval<T>(this IRuleBuilder<T, int> ruleBuilder)
{
return ruleBuilder.SetValidator(new ImportListSyncIntervalValidator());
}
} }
} }