From bd1844030d8af2941af234391cfe5117c194ccc5 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Tue, 4 Jul 2023 19:17:28 +0300 Subject: [PATCH] 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 --- .../EditImportListModalContent.css | 6 +++ .../EditImportListModalContent.css.d.ts | 1 + .../ImportLists/EditImportListModalContent.js | 10 +++++ .../ImportLists/ImportLists/ImportList.js | 11 ++++- .../ImportLists/Options/ImportListOptions.js | 17 -------- .../src/Utilities/Date/formatShortTimeSpan.js | 25 +++++++++++ .../FetchAndParseImportListServiceFixture.cs | 4 +- .../ImportListSyncServiceFixture.cs | 8 ++-- .../Configuration/ConfigService.cs | 7 --- .../Configuration/IConfigService.cs | 1 - .../Datastore/Migration/224_list_sync_time.cs | 18 ++++++++ src/NzbDrone.Core/Datastore/TableMapping.cs | 1 + .../CouchPotato/CouchPotatoImport.cs | 2 + .../FetchAndParseImportListService.cs | 25 +++++++++-- src/NzbDrone.Core/ImportLists/IImportList.cs | 2 + .../ImportLists/ImportListBase.cs | 1 + .../ImportLists/ImportListDefinition.cs | 2 + .../ImportLists/ImportListFactory.cs | 43 +++++++++++++++---- .../ImportLists/ImportListStatus.cs | 4 +- .../ImportLists/ImportListStatusService.cs | 14 +++--- .../ImportLists/Plex/PlexImport.cs | 1 + .../ImportLists/RSSImport/RSSImport.cs | 2 + .../ImportLists/Radarr/RadarrImport.cs | 1 + .../RadarrList/RadarrListImport.cs | 2 + .../RadarrList2/IMDb/IMDbListImport.cs | 2 + .../RadarrList2/StevenLu/StevenLu2Import.cs | 3 ++ .../ImportLists/Rss/Plex/PlexRssImport.cs | 2 + .../ImportLists/StevenLu/StevenLuImport.cs | 2 + .../ImportLists/TMDb/TMDbImportBase.cs | 2 + .../ImportLists/Trakt/TraktImportBase.cs | 1 + src/NzbDrone.Core/Jobs/TaskManager.cs | 16 +------ src/NzbDrone.Core/Localization/Core/en.json | 4 +- .../Config/ImportListConfigController.cs | 3 -- .../Config/ImportListConfigResource.cs | 2 - .../ImportLists/ImportListResource.cs | 4 ++ .../NetImportSyncIntervalValidator.cs | 21 --------- .../Validation/RuleBuilderExtensions.cs | 5 --- 37 files changed, 174 insertions(+), 101 deletions(-) create mode 100644 frontend/src/Utilities/Date/formatShortTimeSpan.js create mode 100644 src/NzbDrone.Core/Datastore/Migration/224_list_sync_time.cs delete mode 100644 src/Radarr.Http/Validation/NetImportSyncIntervalValidator.cs diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css index a2b6014df..8e1c16507 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css @@ -3,3 +3,9 @@ margin-right: auto; } + +.message { + composes: alert from '~Components/Alert.css'; + + margin-bottom: 30px; +} diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css.d.ts b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css.d.ts index c5f0ef8a7..37d918628 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css.d.ts +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css.d.ts @@ -2,6 +2,7 @@ // Please do not change this file! interface CssExports { 'deleteButton': string; + 'message': string; } export const cssExports: CssExports; export default cssExports; diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js index 64ae9f34e..0f52af5fa 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js @@ -14,6 +14,7 @@ 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 formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan'; import translate from 'Utilities/String/translate'; import styles from './EditImportListModalContent.css'; @@ -42,6 +43,7 @@ function EditImportListModalContent(props) { name, enabled, enableAuto, + minRefreshInterval, monitor, minimumAvailability, qualityProfileId, @@ -85,6 +87,14 @@ function EditImportListModalContent(props) { {message.value.message} } + + + {translate('ListWillRefreshEveryInterp', [formatShortTimeSpan(minRefreshInterval.value)])} + + {translate('Name')} diff --git a/frontend/src/Settings/ImportLists/ImportLists/ImportList.js b/frontend/src/Settings/ImportLists/ImportLists/ImportList.js index a177b7384..1494c2132 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/ImportList.js +++ b/frontend/src/Settings/ImportLists/ImportLists/ImportList.js @@ -4,6 +4,7 @@ import Card from 'Components/Card'; import Label from 'Components/Label'; import ConfirmModal from 'Components/Modal/ConfirmModal'; import { kinds } from 'Helpers/Props'; +import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan'; import translate from 'Utilities/String/translate'; import EditImportListModalConnector from './EditImportListModalConnector'; import styles from './ImportList.css'; @@ -56,7 +57,8 @@ class ImportList extends Component { id, name, enabled, - enableAuto + enableAuto, + minRefreshInterval } = this.props; return ( @@ -96,6 +98,12 @@ class ImportList extends Component { } +
+ +
+ - - {translate('ListUpdateInterval')} - - - - 0) { + return `${hours} hour(s)`; + } + + if (minutes > 0) { + return `${minutes} minute(s)`; + } + + return `${seconds} second(s)`; +} + +export default formatShortTimeSpan; diff --git a/src/NzbDrone.Core.Test/ImportListTests/FetchAndParseImportListServiceFixture.cs b/src/NzbDrone.Core.Test/ImportListTests/FetchAndParseImportListServiceFixture.cs index ae1e92b57..e89ed32d7 100644 --- a/src/NzbDrone.Core.Test/ImportListTests/FetchAndParseImportListServiceFixture.cs +++ b/src/NzbDrone.Core.Test/ImportListTests/FetchAndParseImportListServiceFixture.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.ImportListTests _blockedLists = new List(); Mocker.GetMock() - .Setup(v => v.Enabled()) + .Setup(v => v.Enabled(It.IsAny())) .Returns(_importLists); Mocker.GetMock() @@ -183,7 +183,7 @@ namespace NzbDrone.Core.Test.ImportListTests var listResult = Subject.Fetch(); listResult.AnyFailure.Should().BeFalse(); - listResult.Movies.Count.Should().Be(10); + listResult.Movies.Count.Should().Be(5); } } } diff --git a/src/NzbDrone.Core.Test/ImportListTests/ImportListSyncServiceFixture.cs b/src/NzbDrone.Core.Test/ImportListTests/ImportListSyncServiceFixture.cs index 3e937b3c0..6e38839c8 100644 --- a/src/NzbDrone.Core.Test/ImportListTests/ImportListSyncServiceFixture.cs +++ b/src/NzbDrone.Core.Test/ImportListTests/ImportListSyncServiceFixture.cs @@ -72,7 +72,7 @@ namespace NzbDrone.Core.Test.ImportList }; Mocker.GetMock() - .Setup(v => v.Enabled()) + .Setup(v => v.Enabled(It.IsAny())) .Returns(_importLists); Mocker.GetMock() @@ -347,7 +347,7 @@ namespace NzbDrone.Core.Test.ImportList } [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); _importListFetch.Movies.ForEach(m => m.ListId = 1); @@ -384,7 +384,7 @@ namespace NzbDrone.Core.Test.ImportList Subject.Execute(_commandAll); Mocker.GetMock() - .Verify(v => v.AddMovies(It.Is>(s => s.Count == 7 && !s.Any(m => m.TmdbId == _existingMovies[0].TmdbId)), true), Times.Once()); + .Verify(v => v.AddMovies(It.Is>(s => s.Count == 7 && s.All(m => m.TmdbId != _existingMovies[0].TmdbId)), true), Times.Once()); } [Test] @@ -406,7 +406,7 @@ namespace NzbDrone.Core.Test.ImportList Subject.Execute(_commandAll); Mocker.GetMock() - .Verify(v => v.AddMovies(It.Is>(s => s.Count == 7 && !s.Any(m => m.TmdbId == _existingMovies[0].TmdbId)), true), Times.Once()); + .Verify(v => v.AddMovies(It.Is>(s => s.Count == 7 && s.All(m => m.TmdbId != _existingMovies[0].TmdbId)), true), Times.Once()); } } } diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index f9b3cb7d2..43f25e641 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -116,13 +116,6 @@ namespace NzbDrone.Core.Configuration set { SetValue("AvailabilityDelay", value); } } - public int ImportListSyncInterval - { - get { return GetValueInt("ImportListSyncInterval", 24); } - - set { SetValue("ImportListSyncInterval", value); } - } - public string ListSyncLevel { get { return GetValue("ListSyncLevel", "disabled"); } diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs index 686a335a1..41d4245d9 100644 --- a/src/NzbDrone.Core/Configuration/IConfigService.cs +++ b/src/NzbDrone.Core/Configuration/IConfigService.cs @@ -60,7 +60,6 @@ namespace NzbDrone.Core.Configuration bool AllowHardcodedSubs { get; set; } string WhitelistedHardcodedSubs { get; set; } - int ImportListSyncInterval { get; set; } string ListSyncLevel { get; set; } string ImportExclusions { get; set; } diff --git a/src/NzbDrone.Core/Datastore/Migration/224_list_sync_time.cs b/src/NzbDrone.Core/Datastore/Migration/224_list_sync_time.cs new file mode 100644 index 000000000..6bb0083c3 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/224_list_sync_time.cs @@ -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" }); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 9b2d8236c..398c9aaae 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -80,6 +80,7 @@ namespace NzbDrone.Core.Datastore Mapper.Entity("ImportLists").RegisterModel() .Ignore(x => x.ImplementationName) .Ignore(i => i.ListType) + .Ignore(i => i.MinRefreshInterval) .Ignore(i => i.Enable); Mapper.Entity("Notifications").RegisterModel() diff --git a/src/NzbDrone.Core/ImportLists/CouchPotato/CouchPotatoImport.cs b/src/NzbDrone.Core/ImportLists/CouchPotato/CouchPotatoImport.cs index 37f5d28b2..1043f505a 100644 --- a/src/NzbDrone.Core/ImportLists/CouchPotato/CouchPotatoImport.cs +++ b/src/NzbDrone.Core/ImportLists/CouchPotato/CouchPotatoImport.cs @@ -1,3 +1,4 @@ +using System; using NLog; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; @@ -10,6 +11,7 @@ namespace NzbDrone.Core.ImportLists.CouchPotato public override string Name => "CouchPotato"; public override ImportListType ListType => ImportListType.Program; + public override TimeSpan MinRefreshInterval => TimeSpan.FromMinutes(30); public override bool Enabled => true; public override bool EnableAuto => false; diff --git a/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs b/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs index 82d4c67ec..0ec49e039 100644 --- a/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs +++ b/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs @@ -66,6 +66,15 @@ namespace NzbDrone.Core.ImportLists _logger.ProgressInfo("Syncing Movies for List: {0}", importList.Name); 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); if (blockedLists.TryGetValue(importList.Definition.Id, out var blockedListStatus)) @@ -88,7 +97,7 @@ namespace NzbDrone.Core.ImportLists if (!importListReports.AnyFailure) { 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 = listMovies.DistinctBy(x => x.TmdbId).ToList(); @@ -99,6 +108,8 @@ namespace NzbDrone.Core.ImportLists } result.AnyFailure |= importListReports.AnyFailure; + + _importListStatusService.UpdateListSyncStatus(importList.Definition.Id); } } catch (Exception e) @@ -112,6 +123,8 @@ namespace NzbDrone.Core.ImportLists 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); return result; @@ -151,6 +164,8 @@ namespace NzbDrone.Core.ImportLists } result.AnyFailure |= importListReports.AnyFailure; + + _importListStatusService.UpdateListSyncStatus(importList.Definition.Id); } } catch (Exception e) @@ -158,6 +173,8 @@ namespace NzbDrone.Core.ImportLists _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); return result; @@ -166,9 +183,9 @@ namespace NzbDrone.Core.ImportLists private List MapMovieReports(List reports) { 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) - .DistinctBy(x => x.TmdbId) - .ToList(); + .Where(x => x != null) + .DistinctBy(x => x.TmdbId) + .ToList(); _movieMetadataService.UpsertMany(mappedMovies); diff --git a/src/NzbDrone.Core/ImportLists/IImportList.cs b/src/NzbDrone.Core/ImportLists/IImportList.cs index 52be21a56..6f0103621 100644 --- a/src/NzbDrone.Core/ImportLists/IImportList.cs +++ b/src/NzbDrone.Core/ImportLists/IImportList.cs @@ -1,3 +1,4 @@ +using System; using NzbDrone.Core.ThingiProvider; namespace NzbDrone.Core.ImportLists @@ -8,6 +9,7 @@ namespace NzbDrone.Core.ImportLists bool EnableAuto { get; } ImportListType ListType { get; } + TimeSpan MinRefreshInterval { get; } ImportListFetchResult Fetch(); } } diff --git a/src/NzbDrone.Core/ImportLists/ImportListBase.cs b/src/NzbDrone.Core/ImportLists/ImportListBase.cs index 2f989da93..bb5a76ab3 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListBase.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListBase.cs @@ -32,6 +32,7 @@ namespace NzbDrone.Core.ImportLists public abstract string Name { get; } public abstract ImportListType ListType { get; } + public abstract TimeSpan MinRefreshInterval { get; } public abstract bool Enabled { get; } public abstract bool EnableAuto { get; } diff --git a/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs b/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs index 0a8c0eeef..f044a5958 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NzbDrone.Core.Movies; using NzbDrone.Core.ThingiProvider; @@ -21,5 +22,6 @@ namespace NzbDrone.Core.ImportLists public override bool Enable => Enabled; public ImportListType ListType { get; set; } + public TimeSpan MinRefreshInterval { get; set; } } } diff --git a/src/NzbDrone.Core/ImportLists/ImportListFactory.cs b/src/NzbDrone.Core/ImportLists/ImportListFactory.cs index 409680b8a..b2511702b 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListFactory.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListFactory.cs @@ -9,23 +9,24 @@ namespace NzbDrone.Core.ImportLists { public interface IImportListFactory : IProviderFactory { - List Enabled(); + List Enabled(bool filterBlockedImportLists = true); List Discoverable(); } public class ImportListFactory : ProviderFactory, IImportListFactory { - private readonly IImportListRepository _providerRepository; + private readonly IImportListStatusService _importListStatusService; private readonly Logger _logger; - public ImportListFactory(IImportListRepository providerRepository, + public ImportListFactory(IImportListStatusService importListStatusService, + IImportListRepository providerRepository, IEnumerable providers, IServiceProvider container, IEventAggregator eventAggregator, Logger logger) : base(providerRepository, providers, container, eventAggregator, logger) { - _providerRepository = providerRepository; + _importListStatusService = importListStatusService; _logger = logger; } @@ -39,18 +40,42 @@ namespace NzbDrone.Core.ImportLists base.SetProviderCharacteristics(provider, definition); definition.ListType = provider.ListType; + definition.MinRefreshInterval = provider.MinRefreshInterval; } - public List Enabled() + public List Enabled(bool filterBlockedImportLists = true) { - var enabledImporters = GetAvailableProviders().Where(n => ((ImportListDefinition)n.Definition).Enabled); - return enabledImporters.ToList(); + var enabledImportLists = GetAvailableProviders().Where(n => ((ImportListDefinition)n.Definition).Enabled); + + if (filterBlockedImportLists) + { + return FilterBlockedImportLists(enabledImportLists).ToList(); + } + + return enabledImportLists.ToList(); } public List Discoverable() { - var enabledImporters = GetAvailableProviders().Where(n => (n.GetType() == typeof(RadarrList.RadarrListImport) || n.GetType() == typeof(TMDb.Popular.TMDbPopularImport))); - return enabledImporters.ToList(); + var enabledImportLists = GetAvailableProviders().Where(n => n.GetType() == typeof(RadarrList.RadarrListImport) || n.GetType() == typeof(TMDb.Popular.TMDbPopularImport)); + + return enabledImportLists.ToList(); + } + + private IEnumerable FilterBlockedImportLists(IEnumerable 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; + } } } } diff --git a/src/NzbDrone.Core/ImportLists/ImportListStatus.cs b/src/NzbDrone.Core/ImportLists/ImportListStatus.cs index 3d590139a..60d6aa3a0 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListStatus.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListStatus.cs @@ -1,10 +1,10 @@ -using NzbDrone.Core.Movies; +using System; using NzbDrone.Core.ThingiProvider.Status; namespace NzbDrone.Core.ImportLists { public class ImportListStatus : ProviderStatusBase { - public Movie LastSyncListInfo { get; set; } + public DateTime? LastInfoSync { get; set; } } } diff --git a/src/NzbDrone.Core/ImportLists/ImportListStatusService.cs b/src/NzbDrone.Core/ImportLists/ImportListStatusService.cs index bea467650..bbef4b179 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListStatusService.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListStatusService.cs @@ -1,16 +1,16 @@ +using System; using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Movies; using NzbDrone.Core.ThingiProvider.Status; namespace NzbDrone.Core.ImportLists { public interface IImportListStatusService : IProviderStatusServiceBase { - Movie GetLastSyncListInfo(int importListId); + DateTime? GetLastSyncListInfo(int importListId); - void UpdateListSyncStatus(int importListId, Movie listItemInfo); + void UpdateListSyncStatus(int importListId); } public class ImportListStatusService : ProviderStatusServiceBase, 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) { var status = GetProviderStatus(importListId); - status.LastSyncListInfo = listItemInfo; + status.LastInfoSync = DateTime.UtcNow; _providerStatusRepository.Upsert(status); } diff --git a/src/NzbDrone.Core/ImportLists/Plex/PlexImport.cs b/src/NzbDrone.Core/ImportLists/Plex/PlexImport.cs index ceca81b45..7a73c46f6 100644 --- a/src/NzbDrone.Core/ImportLists/Plex/PlexImport.cs +++ b/src/NzbDrone.Core/ImportLists/Plex/PlexImport.cs @@ -15,6 +15,7 @@ namespace NzbDrone.Core.ImportLists.Plex { public readonly IPlexTvService _plexTvService; public override ImportListType ListType => ImportListType.Plex; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(6); public PlexImport(IPlexTvService plexTvService, IHttpClient httpClient, diff --git a/src/NzbDrone.Core/ImportLists/RSSImport/RSSImport.cs b/src/NzbDrone.Core/ImportLists/RSSImport/RSSImport.cs index 19f13cfee..4215d7a93 100644 --- a/src/NzbDrone.Core/ImportLists/RSSImport/RSSImport.cs +++ b/src/NzbDrone.Core/ImportLists/RSSImport/RSSImport.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NLog; using NzbDrone.Common.Http; @@ -12,6 +13,7 @@ namespace NzbDrone.Core.ImportLists.RSSImport public override string Name => "RSS List"; public override ImportListType ListType => ImportListType.Advanced; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); public override bool Enabled => true; public override bool EnableAuto => false; diff --git a/src/NzbDrone.Core/ImportLists/Radarr/RadarrImport.cs b/src/NzbDrone.Core/ImportLists/Radarr/RadarrImport.cs index 1b64e8ac8..8c8689226 100644 --- a/src/NzbDrone.Core/ImportLists/Radarr/RadarrImport.cs +++ b/src/NzbDrone.Core/ImportLists/Radarr/RadarrImport.cs @@ -19,6 +19,7 @@ namespace NzbDrone.Core.ImportLists.Radarr public override bool EnableAuto => false; public override ImportListType ListType => ImportListType.Program; + public override TimeSpan MinRefreshInterval => TimeSpan.FromMinutes(15); public RadarrImport(IRadarrV3Proxy radarrV3Proxy, IImportListStatusService importListStatusService, diff --git a/src/NzbDrone.Core/ImportLists/RadarrList/RadarrListImport.cs b/src/NzbDrone.Core/ImportLists/RadarrList/RadarrListImport.cs index bb118158f..20d349c47 100644 --- a/src/NzbDrone.Core/ImportLists/RadarrList/RadarrListImport.cs +++ b/src/NzbDrone.Core/ImportLists/RadarrList/RadarrListImport.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NLog; using NzbDrone.Common.Http; @@ -12,6 +13,7 @@ namespace NzbDrone.Core.ImportLists.RadarrList public override string Name => "Custom Lists"; public override ImportListType ListType => ImportListType.Advanced; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); public override bool Enabled => true; public override bool EnableAuto => false; diff --git a/src/NzbDrone.Core/ImportLists/RadarrList2/IMDb/IMDbListImport.cs b/src/NzbDrone.Core/ImportLists/RadarrList2/IMDb/IMDbListImport.cs index 813e532d4..41c47bb3b 100644 --- a/src/NzbDrone.Core/ImportLists/RadarrList2/IMDb/IMDbListImport.cs +++ b/src/NzbDrone.Core/ImportLists/RadarrList2/IMDb/IMDbListImport.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NLog; using NzbDrone.Common.Cloud; @@ -15,6 +16,7 @@ namespace NzbDrone.Core.ImportLists.RadarrList2.IMDbList public override string Name => "IMDb Lists"; public override ImportListType ListType => ImportListType.Other; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); public override bool Enabled => true; public override bool EnableAuto => false; diff --git a/src/NzbDrone.Core/ImportLists/RadarrList2/StevenLu/StevenLu2Import.cs b/src/NzbDrone.Core/ImportLists/RadarrList2/StevenLu/StevenLu2Import.cs index 1c0368b50..41e660102 100644 --- a/src/NzbDrone.Core/ImportLists/RadarrList2/StevenLu/StevenLu2Import.cs +++ b/src/NzbDrone.Core/ImportLists/RadarrList2/StevenLu/StevenLu2Import.cs @@ -1,3 +1,4 @@ +using System; using NLog; using NzbDrone.Common.Cloud; using NzbDrone.Common.Http; @@ -13,6 +14,8 @@ namespace NzbDrone.Core.ImportLists.RadarrList2.StevenLu public override string Name => "StevenLu List"; public override ImportListType ListType => ImportListType.Other; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); + public override bool Enabled => true; public override bool EnableAuto => false; diff --git a/src/NzbDrone.Core/ImportLists/Rss/Plex/PlexRssImport.cs b/src/NzbDrone.Core/ImportLists/Rss/Plex/PlexRssImport.cs index b4df67166..91a379c54 100644 --- a/src/NzbDrone.Core/ImportLists/Rss/Plex/PlexRssImport.cs +++ b/src/NzbDrone.Core/ImportLists/Rss/Plex/PlexRssImport.cs @@ -1,3 +1,4 @@ +using System; using NLog; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; @@ -9,6 +10,7 @@ namespace NzbDrone.Core.ImportLists.Rss.Plex { public override string Name => "Plex Watchlist RSS"; public override ImportListType ListType => ImportListType.Plex; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(6); public PlexRssImport(IHttpClient httpClient, IImportListStatusService importListStatusService, diff --git a/src/NzbDrone.Core/ImportLists/StevenLu/StevenLuImport.cs b/src/NzbDrone.Core/ImportLists/StevenLu/StevenLuImport.cs index f04d94bf9..afea86f89 100644 --- a/src/NzbDrone.Core/ImportLists/StevenLu/StevenLuImport.cs +++ b/src/NzbDrone.Core/ImportLists/StevenLu/StevenLuImport.cs @@ -1,3 +1,4 @@ +using System; using NLog; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; @@ -10,6 +11,7 @@ namespace NzbDrone.Core.ImportLists.StevenLu public override string Name => "StevenLu Custom"; public override ImportListType ListType => ImportListType.Advanced; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(24); public override bool Enabled => true; public override bool EnableAuto => false; diff --git a/src/NzbDrone.Core/ImportLists/TMDb/TMDbImportBase.cs b/src/NzbDrone.Core/ImportLists/TMDb/TMDbImportBase.cs index 3c4df1b7c..1dd751012 100644 --- a/src/NzbDrone.Core/ImportLists/TMDb/TMDbImportBase.cs +++ b/src/NzbDrone.Core/ImportLists/TMDb/TMDbImportBase.cs @@ -1,3 +1,4 @@ +using System; using NLog; using NzbDrone.Common.Cloud; using NzbDrone.Common.Http; @@ -11,6 +12,7 @@ namespace NzbDrone.Core.ImportLists.TMDb where TSettings : TMDbSettingsBase, new() { public override ImportListType ListType => ImportListType.TMDB; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); public readonly ISearchForNewMovie _skyhookProxy; public readonly IHttpRequestBuilderFactory _requestBuilder; diff --git a/src/NzbDrone.Core/ImportLists/Trakt/TraktImportBase.cs b/src/NzbDrone.Core/ImportLists/Trakt/TraktImportBase.cs index 330aab737..52e74182e 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/TraktImportBase.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/TraktImportBase.cs @@ -15,6 +15,7 @@ namespace NzbDrone.Core.ImportLists.Trakt public ITraktProxy _traktProxy; private readonly IImportListRepository _importListRepository; public override ImportListType ListType => ImportListType.Trakt; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); protected TraktImportBase(IImportListRepository importListRepository, ITraktProxy traktProxy, diff --git a/src/NzbDrone.Core/Jobs/TaskManager.cs b/src/NzbDrone.Core/Jobs/TaskManager.cs index 78629e4a6..4890c30a5 100644 --- a/src/NzbDrone.Core/Jobs/TaskManager.cs +++ b/src/NzbDrone.Core/Jobs/TaskManager.cs @@ -121,7 +121,7 @@ namespace NzbDrone.Core.Jobs new ScheduledTask { - Interval = GetImportListSyncInterval(), + Interval = 5, TypeName = typeof(ImportListSyncCommand).FullName }, @@ -210,14 +210,6 @@ namespace NzbDrone.Core.Jobs 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) { 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)); rss.Interval = GetRssSyncInterval(); - var importList = _scheduledTaskRepository.GetDefinition(typeof(ImportListSyncCommand)); - importList.Interval = GetImportListSyncInterval(); - var backup = _scheduledTaskRepository.GetDefinition(typeof(BackupCommand)); backup.Interval = GetBackupInterval(); var refreshMonitoredDownloads = _scheduledTaskRepository.GetDefinition(typeof(RefreshMonitoredDownloadsCommand)); refreshMonitoredDownloads.Interval = GetRefreshMonitoredInterval(); - _scheduledTaskRepository.UpdateMany(new List { rss, importList, refreshMonitoredDownloads, backup }); + _scheduledTaskRepository.UpdateMany(new List { rss, refreshMonitoredDownloads, backup }); _cache.Find(rss.TypeName).Interval = rss.Interval; - _cache.Find(importList.TypeName).Interval = importList.Interval; _cache.Find(backup.TypeName).Interval = backup.Interval; _cache.Find(refreshMonitoredDownloads.TypeName).Interval = refreshMonitoredDownloads.Interval; } diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 75a95d0ad..4f3c3ce50 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -416,7 +416,6 @@ "ImportListMultipleMissingRoots": "Multiple root folders are missing for import lists: {0}", "ImportListStatusCheckAllClientMessage": "All lists are unavailable due to failures", "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", "ImportMovies": "Import Movies", "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", "List": "List", "ListExclusions": "List Exclusions", + "ListRefreshInterval": "List Refresh Interval", "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)", "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", - "ListUpdateInterval": "List Update Interval", + "ListWillRefreshEveryInterp": "List will refresh every {0}", "Lists": "Lists", "ListsSettingsSummary": "Import Lists, list exclusions", "Loading": "Loading", diff --git a/src/Radarr.Api.V3/Config/ImportListConfigController.cs b/src/Radarr.Api.V3/Config/ImportListConfigController.cs index e6a176376..a60a42a2f 100644 --- a/src/Radarr.Api.V3/Config/ImportListConfigController.cs +++ b/src/Radarr.Api.V3/Config/ImportListConfigController.cs @@ -1,6 +1,5 @@ using NzbDrone.Core.Configuration; using Radarr.Http; -using Radarr.Http.Validation; namespace Radarr.Api.V3.Config { @@ -11,8 +10,6 @@ namespace Radarr.Api.V3.Config public ImportListConfigController(IConfigService configService) : base(configService) { - SharedValidator.RuleFor(c => c.ImportListSyncInterval) - .IsValidImportListSyncInterval(); } protected override ImportListConfigResource ToResource(IConfigService model) diff --git a/src/Radarr.Api.V3/Config/ImportListConfigResource.cs b/src/Radarr.Api.V3/Config/ImportListConfigResource.cs index 2af075320..a7c3ed309 100644 --- a/src/Radarr.Api.V3/Config/ImportListConfigResource.cs +++ b/src/Radarr.Api.V3/Config/ImportListConfigResource.cs @@ -5,7 +5,6 @@ namespace Radarr.Api.V3.Config { public class ImportListConfigResource : RestResource { - public int ImportListSyncInterval { get; set; } public string ListSyncLevel { get; set; } public string ImportExclusions { get; set; } } @@ -16,7 +15,6 @@ namespace Radarr.Api.V3.Config { return new ImportListConfigResource { - ImportListSyncInterval = model.ImportListSyncInterval, ListSyncLevel = model.ListSyncLevel, ImportExclusions = model.ImportExclusions }; diff --git a/src/Radarr.Api.V3/ImportLists/ImportListResource.cs b/src/Radarr.Api.V3/ImportLists/ImportListResource.cs index a97f3007d..95cee0aa2 100644 --- a/src/Radarr.Api.V3/ImportLists/ImportListResource.cs +++ b/src/Radarr.Api.V3/ImportLists/ImportListResource.cs @@ -1,3 +1,4 @@ +using System; using NzbDrone.Core.ImportLists; using NzbDrone.Core.Movies; @@ -14,6 +15,7 @@ namespace Radarr.Api.V3.ImportLists public MovieStatusType MinimumAvailability { get; set; } public ImportListType ListType { get; set; } public int ListOrder { get; set; } + public TimeSpan MinRefreshInterval { get; set; } } public class ImportListResourceMapper : ProviderResourceMapper @@ -36,6 +38,7 @@ namespace Radarr.Api.V3.ImportLists resource.MinimumAvailability = definition.MinimumAvailability; resource.ListType = definition.ListType; resource.ListOrder = (int)definition.ListType; + resource.MinRefreshInterval = definition.MinRefreshInterval; return resource; } @@ -57,6 +60,7 @@ namespace Radarr.Api.V3.ImportLists definition.ProfileId = resource.QualityProfileId; definition.MinimumAvailability = resource.MinimumAvailability; definition.ListType = resource.ListType; + definition.MinRefreshInterval = resource.MinRefreshInterval; return definition; } diff --git a/src/Radarr.Http/Validation/NetImportSyncIntervalValidator.cs b/src/Radarr.Http/Validation/NetImportSyncIntervalValidator.cs deleted file mode 100644 index fcbb691a9..000000000 --- a/src/Radarr.Http/Validation/NetImportSyncIntervalValidator.cs +++ /dev/null @@ -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; - } - } -} diff --git a/src/Radarr.Http/Validation/RuleBuilderExtensions.cs b/src/Radarr.Http/Validation/RuleBuilderExtensions.cs index 4fe1a5c39..4c7b78c48 100644 --- a/src/Radarr.Http/Validation/RuleBuilderExtensions.cs +++ b/src/Radarr.Http/Validation/RuleBuilderExtensions.cs @@ -36,10 +36,5 @@ namespace Radarr.Http.Validation { return ruleBuilder.SetValidator(new RssSyncIntervalValidator()); } - - public static IRuleBuilderOptions IsValidImportListSyncInterval(this IRuleBuilder ruleBuilder) - { - return ruleBuilder.SetValidator(new ImportListSyncIntervalValidator()); - } } }