diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css index 23e22b6dc..ced336990 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.css @@ -13,3 +13,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 831b792ce..b3f1a027f 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js @@ -20,6 +20,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 translate from 'Utilities/String/translate'; import styles from './EditImportListModalContent.css'; @@ -74,6 +75,7 @@ function EditImportListModalContent(props) { id, name, enableAutomaticAdd, + minRefreshInterval, shouldMonitor, shouldMonitorExisting, shouldSearch, @@ -118,6 +120,13 @@ function EditImportListModalContent(props) { } + + {translate('ListWillRefreshEveryInterp', [formatShortTimeSpan(minRefreshInterval.value)])} + +
diff --git a/frontend/src/Settings/ImportLists/ImportLists/ImportList.js b/frontend/src/Settings/ImportLists/ImportLists/ImportList.js index 41dff3fa5..639d84f58 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,6 +57,7 @@ class ImportList extends Component { id, name, enableAutomaticAdd, + minRefreshInterval, shouldSearch } = this.props; @@ -85,6 +87,15 @@ 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/Lidarr.Api.V1/ImportLists/ImportListResource.cs b/src/Lidarr.Api.V1/ImportLists/ImportListResource.cs index 6a9401b45..e6a56bd21 100644 --- a/src/Lidarr.Api.V1/ImportLists/ImportListResource.cs +++ b/src/Lidarr.Api.V1/ImportLists/ImportListResource.cs @@ -1,3 +1,4 @@ +using System; using NzbDrone.Core.ImportLists; using NzbDrone.Core.Music; @@ -15,6 +16,7 @@ namespace Lidarr.Api.V1.ImportLists public int MetadataProfileId { get; set; } public ImportListType ListType { get; set; } public int ListOrder { get; set; } + public TimeSpan MinRefreshInterval { get; set; } } public class ImportListResourceMapper : ProviderResourceMapper @@ -38,6 +40,7 @@ namespace Lidarr.Api.V1.ImportLists resource.MetadataProfileId = definition.MetadataProfileId; resource.ListType = definition.ListType; resource.ListOrder = (int)definition.ListType; + resource.MinRefreshInterval = definition.MinRefreshInterval; return resource; } @@ -60,6 +63,7 @@ namespace Lidarr.Api.V1.ImportLists definition.ProfileId = resource.QualityProfileId; definition.MetadataProfileId = resource.MetadataProfileId; definition.ListType = resource.ListType; + definition.MinRefreshInterval = resource.MinRefreshInterval; return definition; } diff --git a/src/NzbDrone.Core/Datastore/Migration/069_list_sync_time.cs b/src/NzbDrone.Core/Datastore/Migration/069_list_sync_time.cs new file mode 100644 index 000000000..816e4103a --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/069_list_sync_time.cs @@ -0,0 +1,16 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(069)] + public class list_sync_time : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Delete.Column("LastSyncListInfo").FromTable("ImportListStatus"); + + Alter.Table("ImportListStatus").AddColumn("LastInfoSync").AsDateTimeOffset().Nullable(); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index ca9052f58..54e575a06 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -75,8 +75,9 @@ namespace NzbDrone.Core.Datastore Mapper.Entity("ImportLists").RegisterModel() .Ignore(x => x.ImplementationName) - .Ignore(i => i.Enable) - .Ignore(i => i.ListType); + .Ignore(i => i.ListType) + .Ignore(i => i.MinRefreshInterval) + .Ignore(i => i.Enable); Mapper.Entity("Notifications").RegisterModel() .Ignore(x => x.ImplementationName) diff --git a/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs b/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs index 53996101a..afce13495 100644 --- a/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs +++ b/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using FluentValidation.Results; using NLog; @@ -14,6 +15,7 @@ namespace NzbDrone.Core.ImportLists.Custom public override string Name => "Custom List"; public override ImportListType ListType => ImportListType.Advanced; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(6); public CustomImport(ICustomImportProxy customProxy, IImportListStatusService importListStatusService, diff --git a/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs b/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs index fc1dbd3c8..cda3873b7 100644 --- a/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs +++ b/src/NzbDrone.Core/ImportLists/FetchAndParseImportListService.cs @@ -17,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; } @@ -45,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(() => { @@ -58,6 +67,8 @@ namespace NzbDrone.Core.ImportLists result.AddRange(importListReports); } + + _importListStatusService.UpdateListSyncStatus(importList.Definition.Id); } catch (Exception e) { @@ -89,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/HeadphonesImport/HeadphonesImport.cs b/src/NzbDrone.Core/ImportLists/HeadphonesImport/HeadphonesImport.cs index f587649b4..3de5eae9c 100644 --- a/src/NzbDrone.Core/ImportLists/HeadphonesImport/HeadphonesImport.cs +++ b/src/NzbDrone.Core/ImportLists/HeadphonesImport/HeadphonesImport.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.HeadphonesImport public override string Name => "Headphones"; public override ImportListType ListType => ImportListType.Other; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(1); public override int PageSize => 1000; 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/ImportListBase.cs b/src/NzbDrone.Core/ImportLists/ImportListBase.cs index a2ec01bcc..268690b13 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListBase.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListBase.cs @@ -22,6 +22,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 4a1e8b243..fdab93dff 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs @@ -1,3 +1,4 @@ +using System; using NzbDrone.Core.Music; using NzbDrone.Core.ThingiProvider; @@ -18,6 +19,7 @@ namespace NzbDrone.Core.ImportLists public ImportListStatus Status { get; set; } public ImportListType ListType { get; set; } + public TimeSpan MinRefreshInterval { get; set; } } public enum ImportListMonitorType diff --git a/src/NzbDrone.Core/ImportLists/ImportListFactory.cs b/src/NzbDrone.Core/ImportLists/ImportListFactory.cs index 65af19520..5ac6135c1 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListFactory.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListFactory.cs @@ -40,6 +40,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..1ebc84fe3 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListStatus.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListStatus.cs @@ -1,10 +1,10 @@ -using NzbDrone.Core.Parser.Model; +using System; using NzbDrone.Core.ThingiProvider.Status; 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/LastFm/LastFmTag.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmTag.cs index 727c3d1dc..92cd2b430 100644 --- a/src/NzbDrone.Core/ImportLists/LastFm/LastFmTag.cs +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmTag.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.LastFm public override string Name => "Last.fm Tag"; public override ImportListType ListType => ImportListType.LastFm; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); public override int PageSize => 1000; diff --git a/src/NzbDrone.Core/ImportLists/LastFm/LastFmUser.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmUser.cs index 884062aa7..3b8ed9db2 100644 --- a/src/NzbDrone.Core/ImportLists/LastFm/LastFmUser.cs +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmUser.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.LastFm public override string Name => "Last.fm User"; public override ImportListType ListType => ImportListType.LastFm; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); public override int PageSize => 1000; diff --git a/src/NzbDrone.Core/ImportLists/Lidarr/LidarrImport.cs b/src/NzbDrone.Core/ImportLists/Lidarr/LidarrImport.cs index c85b8683b..836269ca1 100644 --- a/src/NzbDrone.Core/ImportLists/Lidarr/LidarrImport.cs +++ b/src/NzbDrone.Core/ImportLists/Lidarr/LidarrImport.cs @@ -17,6 +17,7 @@ namespace NzbDrone.Core.ImportLists.Lidarr public override string Name => "Lidarr"; public override ImportListType ListType => ImportListType.Program; + public override TimeSpan MinRefreshInterval => TimeSpan.FromMinutes(15); public LidarrImport(ILidarrV1Proxy lidarrV1Proxy, IImportListStatusService importListStatusService, diff --git a/src/NzbDrone.Core/ImportLists/LidarrLists/LidarrLists.cs b/src/NzbDrone.Core/ImportLists/LidarrLists/LidarrLists.cs index 052c39fec..b2722a5bf 100644 --- a/src/NzbDrone.Core/ImportLists/LidarrLists/LidarrLists.cs +++ b/src/NzbDrone.Core/ImportLists/LidarrLists/LidarrLists.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NLog; using NzbDrone.Common.Http; @@ -13,6 +14,7 @@ namespace NzbDrone.Core.ImportLists.LidarrLists public override string Name => "Lidarr Lists"; public override ImportListType ListType => ImportListType.Other; + public override TimeSpan MinRefreshInterval => TimeSpan.FromMinutes(15); public override int PageSize => 10; diff --git a/src/NzbDrone.Core/ImportLists/MusicBrainzSeries/MusicBrainzSeries.cs b/src/NzbDrone.Core/ImportLists/MusicBrainzSeries/MusicBrainzSeries.cs index b7852620f..8bcc68d35 100644 --- a/src/NzbDrone.Core/ImportLists/MusicBrainzSeries/MusicBrainzSeries.cs +++ b/src/NzbDrone.Core/ImportLists/MusicBrainzSeries/MusicBrainzSeries.cs @@ -1,3 +1,4 @@ +using System; using NLog; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; @@ -14,6 +15,7 @@ namespace NzbDrone.Core.ImportLists.MusicBrainzSeries public override ProviderMessage Message => new ProviderMessage("MusicBrainz Series only supports release groups within series, other types of member will not be picked up by Lidarr", ProviderMessageType.Warning); public override ImportListType ListType => ImportListType.Other; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); private readonly IMetadataRequestBuilder _requestBuilder; diff --git a/src/NzbDrone.Core/ImportLists/Spotify/SpotifyImportListBase.cs b/src/NzbDrone.Core/ImportLists/Spotify/SpotifyImportListBase.cs index 57e6ec9df..9ae356ba1 100644 --- a/src/NzbDrone.Core/ImportLists/Spotify/SpotifyImportListBase.cs +++ b/src/NzbDrone.Core/ImportLists/Spotify/SpotifyImportListBase.cs @@ -45,6 +45,7 @@ namespace NzbDrone.Core.ImportLists.Spotify } public override ImportListType ListType => ImportListType.Spotify; + public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12); public string AccessToken => Settings.AccessToken; diff --git a/src/NzbDrone.Core/Jobs/TaskManager.cs b/src/NzbDrone.Core/Jobs/TaskManager.cs index 6c4824a7b..57fdcc622 100644 --- a/src/NzbDrone.Core/Jobs/TaskManager.cs +++ b/src/NzbDrone.Core/Jobs/TaskManager.cs @@ -116,7 +116,7 @@ namespace NzbDrone.Core.Jobs new ScheduledTask { - Interval = 24 * 60, // TODO: Add a setting? + Interval = 5, TypeName = typeof(ImportListSyncCommand).FullName }, diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index da606d4ea..b6b202fe2 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -453,6 +453,8 @@ "LidarrSupportsAnyIndexerThatUsesTheNewznabStandardAsWellAsOtherIndexersListedBelow": "Lidarr supports any indexer that uses the Newznab standard, as well as other indexers listed below.", "LidarrSupportsMultipleListsForImportingAlbumsAndArtistsIntoTheDatabase": "Lidarr supports multiple lists for importing Albums and Artists into the database.", "LidarrTags": "Lidarr Tags", + "ListRefreshInterval": "List Refresh Interval", + "ListWillRefreshEveryInterp": "List will refresh every {0}", "Loading": "loading", "LoadingAlbumsFailed": "Loading albums failed", "LoadingTrackFilesFailed": "Loading track files failed",