From c522cd120d08757e7e43c2348be4d7f05a254fac Mon Sep 17 00:00:00 2001 From: Qstick Date: Wed, 7 Dec 2022 21:09:42 -0600 Subject: [PATCH] New: Rework List sync interval logic * New: Rework List sync interval logic Fixes #5011 --- .../EditImportListModalContent.css | 6 +++++ .../ImportLists/EditImportListModalContent.js | 11 ++++++++ .../ImportLists/ImportLists/ImportList.js | 11 +++++++- .../src/Utilities/Date/formatShortTimeSpan.js | 25 +++++++++++++++++++ .../Datastore/Migration/178_list_sync_time.cs | 17 +++++++++++++ src/NzbDrone.Core/Datastore/TableMapping.cs | 1 + .../ImportLists/Custom/CustomImport.cs | 6 ++--- .../FetchAndParseImportListService.cs | 22 ++++++++++++++-- src/NzbDrone.Core/ImportLists/IImportList.cs | 2 ++ .../ImportLists/Imdb/ImdbListImport.cs | 2 ++ .../ImportLists/ImportListBase.cs | 3 +++ .../ImportLists/ImportListDefinition.cs | 2 ++ .../ImportLists/ImportListFactory.cs | 1 + .../ImportLists/ImportListStatus.cs | 3 ++- .../ImportLists/ImportListStatusService.cs | 14 +++++------ .../ImportLists/Plex/PlexImport.cs | 2 ++ .../ImportLists/Sonarr/SonarrImport.cs | 1 + .../ImportLists/Trakt/TraktImportBase.cs | 1 + src/NzbDrone.Core/Jobs/TaskManager.cs | 2 +- .../ImportLists/ImportListResource.cs | 4 +++ 20 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 frontend/src/Utilities/Date/formatShortTimeSpan.js create mode 100644 src/NzbDrone.Core/Datastore/Migration/178_list_sync_time.cs diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css index fd7ddf093..a46b0d4ce 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css @@ -7,3 +7,9 @@ .labelIcon { margin-left: 8px; } + +.message { + composes: alert from '~Components/Alert.css'; + + margin-bottom: 30px; +} diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js index e71c680fc..fc632d40b 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import SeriesMonitoringOptionsPopoverContent from 'AddSeries/SeriesMonitoringOptionsPopoverContent'; import SeriesTypePopoverContent from 'AddSeries/SeriesTypePopoverContent'; +import Alert from 'Components/Alert'; import Form from 'Components/Form/Form'; import FormGroup from 'Components/Form/FormGroup'; import FormInputGroup from 'Components/Form/FormInputGroup'; @@ -17,6 +18,7 @@ import ModalFooter from 'Components/Modal/ModalFooter'; import ModalHeader from 'Components/Modal/ModalHeader'; import Popover from 'Components/Tooltip/Popover'; import { icons, inputTypes, kinds, tooltipPositions } from 'Helpers/Props'; +import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan'; import styles from './EditImportListModalContent.css'; function EditImportListModalContent(props) { @@ -42,6 +44,7 @@ function EditImportListModalContent(props) { id, name, enableAutomaticAdd, + minRefreshInterval, shouldMonitor, rootFolderPath, qualityProfileId, @@ -73,6 +76,14 @@ function EditImportListModalContent(props) { { !isFetching && !error ?
+ + + {`List will refresh every ${formatShortTimeSpan(minRefreshInterval.value)}`} + + Name diff --git a/frontend/src/Settings/ImportLists/ImportLists/ImportList.js b/frontend/src/Settings/ImportLists/ImportLists/ImportList.js index 57f20fef7..fb49c6345 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 EditImportListModalConnector from './EditImportListModalConnector'; import styles from './ImportList.css'; @@ -54,7 +55,8 @@ class ImportList extends Component { const { id, name, - enableAutomaticAdd + enableAutomaticAdd, + minRefreshInterval } = this.props; return ( @@ -77,6 +79,12 @@ class ImportList extends Component { +
+ +
+ 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/Datastore/Migration/178_list_sync_time.cs b/src/NzbDrone.Core/Datastore/Migration/178_list_sync_time.cs new file mode 100644 index 000000000..8f72f0518 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/178_list_sync_time.cs @@ -0,0 +1,17 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; +using NzbDrone.Core.Languages; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(178)] + public class list_sync_time : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Delete.Column("LastSyncListInfo").FromTable("ImportListStatus"); + + Alter.Table("ImportListStatus").AddColumn("LastInfoSync").AsDateTime().Nullable(); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 538a244d6..6ee369eed 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -79,6 +79,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/Custom/CustomImport.cs b/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs index 5cf161867..93f71e6d8 100644 --- a/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs +++ b/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs @@ -1,9 +1,7 @@ +using System; using System.Collections.Generic; - using FluentValidation.Results; - using NLog; - using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Parser; @@ -16,6 +14,8 @@ namespace NzbDrone.Core.ImportLists.Custom private readonly ICustomImportProxy _customProxy; public override string Name => "Custom List"; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(6); + public override ImportListType ListType => ImportListType.Advanced; public CustomImport(ICustomImportProxy customProxy, diff --git a/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs b/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs index 7409b2851..6fd5c1bc5 100644 --- a/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs +++ b/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NLog; -using NzbDrone.Common.Extensions; using NzbDrone.Common.TPL; using NzbDrone.Core.Parser.Model; @@ -18,11 +17,13 @@ namespace NzbDrone.Core.ImportLists public class FetchAndParseImportListService : IFetchAndParseImportList { private readonly IImportListFactory _importListFactory; + private readonly IImportListStatusService _importListStatusService; private readonly Logger _logger; - public FetchAndParseImportListService(IImportListFactory importListFactory, Logger logger) + public FetchAndParseImportListService(IImportListFactory importListFactory, IImportListStatusService importListStatusService, Logger logger) { _importListFactory = importListFactory; + _importListStatusService = importListStatusService; _logger = logger; } @@ -46,6 +47,13 @@ namespace NzbDrone.Core.ImportLists foreach (var importList in importLists) { var importListLocal = importList; + var importListStatus = _importListStatusService.GetLastSyncListInfo(importListLocal.Definition.Id); + + if (DateTime.UtcNow < (importListStatus + importListLocal.MinRefreshInterval)) + { + _logger.Trace("Skipping refresh of Import List {0} due to minimum refresh inverval", importListLocal.Definition.Name); + continue; + } var task = taskFactory.StartNew(() => { @@ -59,6 +67,8 @@ namespace NzbDrone.Core.ImportLists result.AddRange(importListReports); } + + _importListStatusService.UpdateListSyncStatus(importList.Definition.Id); } catch (Exception e) { @@ -90,6 +100,14 @@ namespace NzbDrone.Core.ImportLists return result; } + var importListStatus = _importListStatusService.GetLastSyncListInfo(importList.Definition.Id); + + if (DateTime.UtcNow < (importListStatus + importList.MinRefreshInterval)) + { + _logger.Trace("Skipping refresh of Import List {0} due to minimum refresh inverval", importList.Definition.Name); + return result; + } + var taskList = new List(); var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None); diff --git a/src/NzbDrone.Core/ImportLists/IImportList.cs b/src/NzbDrone.Core/ImportLists/IImportList.cs index f9fde642b..4970c5261 100644 --- a/src/NzbDrone.Core/ImportLists/IImportList.cs +++ b/src/NzbDrone.Core/ImportLists/IImportList.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; @@ -7,6 +8,7 @@ namespace NzbDrone.Core.ImportLists public interface IImportList : IProvider { ImportListType ListType { get; } + TimeSpan MinRefreshInterval { get; } IList Fetch(); } } diff --git a/src/NzbDrone.Core/ImportLists/Imdb/ImdbListImport.cs b/src/NzbDrone.Core/ImportLists/Imdb/ImdbListImport.cs index ca593f3fc..bba85c407 100644 --- a/src/NzbDrone.Core/ImportLists/Imdb/ImdbListImport.cs +++ b/src/NzbDrone.Core/ImportLists/Imdb/ImdbListImport.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.Imdb public override string Name => "IMDb Lists"; public override ImportListType ListType => ImportListType.Other; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); public ImdbListImport(IHttpClient httpClient, IImportListStatusService importListStatusService, diff --git a/src/NzbDrone.Core/ImportLists/ImportListBase.cs b/src/NzbDrone.Core/ImportLists/ImportListBase.cs index e47e9972d..8460e6d94 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListBase.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListBase.cs @@ -5,6 +5,7 @@ using FluentValidation.Results; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Indexers; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; @@ -23,6 +24,8 @@ namespace NzbDrone.Core.ImportLists public abstract ImportListType ListType { get; } + public abstract TimeSpan MinRefreshInterval { get; } + public ImportListBase(IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, Logger logger) { _importListStatusService = importListStatusService; diff --git a/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs b/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs index 0d38d3fea..48fb511d9 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs @@ -1,3 +1,4 @@ +using System; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Tv; @@ -16,5 +17,6 @@ namespace NzbDrone.Core.ImportLists public ImportListStatus Status { get; set; } 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 aed9ad44d..211ccb413 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListFactory.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListFactory.cs @@ -41,6 +41,7 @@ namespace NzbDrone.Core.ImportLists base.SetProviderCharacteristics(provider, definition); definition.ListType = provider.ListType; + definition.MinRefreshInterval = provider.MinRefreshInterval; } public List AutomaticAddEnabled(bool filterBlockedImportLists = true) diff --git a/src/NzbDrone.Core/ImportLists/ImportListStatus.cs b/src/NzbDrone.Core/ImportLists/ImportListStatus.cs index 139d2b97b..69892e1b5 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListStatus.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListStatus.cs @@ -1,3 +1,4 @@ +using System; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider.Status; @@ -5,6 +6,6 @@ namespace NzbDrone.Core.ImportLists { public class ImportListStatus : ProviderStatusBase { - public ImportListItemInfo 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 362359dca..e543d0a7e 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.Parser.Model; using NzbDrone.Core.ThingiProvider.Status; namespace NzbDrone.Core.ImportLists { public interface IImportListStatusService : IProviderStatusServiceBase { - ImportListItemInfo GetLastSyncListInfo(int importListId); + DateTime GetLastSyncListInfo(int importListId); - void UpdateListSyncStatus(int importListId, ImportListItemInfo listItemInfo); + void UpdateListSyncStatus(int importListId); } public class ImportListStatusService : ProviderStatusServiceBase, IImportListStatusService @@ -20,18 +20,18 @@ namespace NzbDrone.Core.ImportLists { } - public ImportListItemInfo GetLastSyncListInfo(int importListId) + public DateTime GetLastSyncListInfo(int importListId) { - return GetProviderStatus(importListId).LastSyncListInfo; + return GetProviderStatus(importListId).LastInfoSync; } - public void UpdateListSyncStatus(int importListId, ImportListItemInfo 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 c99d7a21b..c1f5f61e8 100644 --- a/src/NzbDrone.Core/ImportLists/Plex/PlexImport.cs +++ b/src/NzbDrone.Core/ImportLists/Plex/PlexImport.cs @@ -15,7 +15,9 @@ namespace NzbDrone.Core.ImportLists.Plex public class PlexImport : HttpImportListBase { 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/Sonarr/SonarrImport.cs b/src/NzbDrone.Core/ImportLists/Sonarr/SonarrImport.cs index 34a8ad546..6159b71c8 100644 --- a/src/NzbDrone.Core/ImportLists/Sonarr/SonarrImport.cs +++ b/src/NzbDrone.Core/ImportLists/Sonarr/SonarrImport.cs @@ -17,6 +17,7 @@ namespace NzbDrone.Core.ImportLists.Sonarr public override string Name => "Sonarr"; public override ImportListType ListType => ImportListType.Program; + public override TimeSpan MinRefreshInterval => TimeSpan.FromMinutes(5); public SonarrImport(ISonarrV3Proxy sonarrV3Proxy, IImportListStatusService importListStatusService, diff --git a/src/NzbDrone.Core/ImportLists/Trakt/TraktImportBase.cs b/src/NzbDrone.Core/ImportLists/Trakt/TraktImportBase.cs index acad8918c..da8330c7a 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/TraktImportBase.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/TraktImportBase.cs @@ -14,6 +14,7 @@ namespace NzbDrone.Core.ImportLists.Trakt where TSettings : TraktSettingsBase, new() { public override ImportListType ListType => ImportListType.Trakt; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); public const string OAuthUrl = "https://trakt.tv/oauth/authorize"; public const string RedirectUri = "https://auth.servarr.com/v1/trakt_sonarr/auth"; diff --git a/src/NzbDrone.Core/Jobs/TaskManager.cs b/src/NzbDrone.Core/Jobs/TaskManager.cs index 7c7eda9b9..1423a0281 100644 --- a/src/NzbDrone.Core/Jobs/TaskManager.cs +++ b/src/NzbDrone.Core/Jobs/TaskManager.cs @@ -117,7 +117,7 @@ namespace NzbDrone.Core.Jobs new ScheduledTask { - Interval = 24 * 60, + Interval = 5, TypeName = typeof(ImportListSyncCommand).FullName }, diff --git a/src/Sonarr.Api.V3/ImportLists/ImportListResource.cs b/src/Sonarr.Api.V3/ImportLists/ImportListResource.cs index 040961caf..dad382ca2 100644 --- a/src/Sonarr.Api.V3/ImportLists/ImportListResource.cs +++ b/src/Sonarr.Api.V3/ImportLists/ImportListResource.cs @@ -1,3 +1,4 @@ +using System; using NzbDrone.Core.ImportLists; using NzbDrone.Core.Tv; @@ -13,6 +14,7 @@ namespace Sonarr.Api.V3.ImportLists public bool SeasonFolder { get; set; } public ImportListType ListType { get; set; } public int ListOrder { get; set; } + public TimeSpan MinRefreshInterval { get; set; } } public class ImportListResourceMapper : ProviderResourceMapper @@ -34,6 +36,7 @@ namespace Sonarr.Api.V3.ImportLists resource.SeasonFolder = definition.SeasonFolder; resource.ListType = definition.ListType; resource.ListOrder = (int)definition.ListType; + resource.MinRefreshInterval = definition.MinRefreshInterval; return resource; } @@ -54,6 +57,7 @@ namespace Sonarr.Api.V3.ImportLists definition.SeriesType = resource.SeriesType; definition.SeasonFolder = resource.SeasonFolder; definition.ListType = resource.ListType; + definition.MinRefreshInterval = resource.MinRefreshInterval; return definition; }