mirror of
https://github.com/Radarr/Radarr.git
synced 2025-04-24 06:27:08 -04:00
New: Option to treat downloads with non-media extensions as failed
(cherry picked from commit 776143cc813ec1b5fa31fbf8667c3ab174b71f5c) New: Treat .scr as dangerous file (cherry picked from commit 103ccd74f30830944e9e9f06d02be096f476ae34) New: .arj and .lzh extensions are potentially dangerous (cherry picked from commit a72288a14e67f62b00f921dbeb1a0f57a61e5ba7) Fixed: Failing dangerous and executable single file downloads (cherry picked from commit e37684e045310ca543aa6a22b38a325cd8a8e84d) Fixed: Rejected Imports with no associated release or indexer (cherry picked from commit 31e02bdeada8c85d67a75b69e57d3e7ea46989c6) Fixed: Don't return warning in title field for rejected downloads (cherry picked from commit 1fa532dd3eaaee01ac6a049e43fcdbd44357d617) Fixed: Improve rejected download handling (cherry picked from commit 4db43882361232eb8fe9ee5331c3d77ea3aa8dfa)
This commit is contained in:
parent
91f08a83cd
commit
48075e33ac
24 changed files with 294 additions and 58 deletions
|
@ -1,8 +1,10 @@
|
|||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Torznab;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
|
@ -14,13 +16,13 @@ namespace NzbDrone.Core.Test.IndexerTests
|
|||
[Test]
|
||||
public void should_not_return_config_for_non_existent_indexer()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(v => v.Get(It.IsAny<int>()))
|
||||
.Throws(new ModelNotFoundException(typeof(IndexerDefinition), 0));
|
||||
Mocker.GetMock<ICachedIndexerSettingsProvider>()
|
||||
.Setup(v => v.GetSettings(It.IsAny<int>()))
|
||||
.Returns<CachedIndexerSettings>(null);
|
||||
|
||||
var result = Subject.GetSeedConfiguration(new RemoteMovie
|
||||
{
|
||||
Release = new ReleaseInfo()
|
||||
Release = new ReleaseInfo
|
||||
{
|
||||
DownloadProtocol = DownloadProtocol.Torrent,
|
||||
IndexerId = 0
|
||||
|
@ -29,5 +31,33 @@ namespace NzbDrone.Core.Test.IndexerTests
|
|||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_seed_time_for_movies()
|
||||
{
|
||||
var settings = new TorznabSettings();
|
||||
settings.SeedCriteria.SeedTime = 10;
|
||||
|
||||
Mocker.GetMock<ICachedIndexerSettingsProvider>()
|
||||
.Setup(v => v.GetSettings(It.IsAny<int>()))
|
||||
.Returns(new CachedIndexerSettings
|
||||
{
|
||||
FailDownloads = new HashSet<FailDownloads> { FailDownloads.Executables },
|
||||
SeedCriteriaSettings = settings.SeedCriteria
|
||||
});
|
||||
|
||||
var result = Subject.GetSeedConfiguration(new RemoteMovie
|
||||
{
|
||||
Release = new ReleaseInfo
|
||||
{
|
||||
DownloadProtocol = DownloadProtocol.Torrent,
|
||||
IndexerId = 1
|
||||
},
|
||||
ParsedMovieInfo = new ParsedMovieInfo()
|
||||
});
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result.SeedTime.Should().Be(TimeSpan.FromMinutes(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,5 +15,6 @@ namespace NzbDrone.Core.Test.IndexerTests
|
|||
public string BaseUrl { get; set; }
|
||||
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
public IEnumerable<int> FailDownloads { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace NzbDrone.Core.Download
|
|||
private readonly IParsingService _parsingService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly ITrackedDownloadAlreadyImported _trackedDownloadAlreadyImported;
|
||||
private readonly IRejectedImportService _rejectedImportService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public CompletedDownloadService(IEventAggregator eventAggregator,
|
||||
|
@ -42,6 +43,7 @@ namespace NzbDrone.Core.Download
|
|||
IParsingService parsingService,
|
||||
IMovieService movieService,
|
||||
ITrackedDownloadAlreadyImported trackedDownloadAlreadyImported,
|
||||
IRejectedImportService rejectedImportService,
|
||||
Logger logger)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
|
@ -51,6 +53,7 @@ namespace NzbDrone.Core.Download
|
|||
_parsingService = parsingService;
|
||||
_movieService = movieService;
|
||||
_trackedDownloadAlreadyImported = trackedDownloadAlreadyImported;
|
||||
_rejectedImportService = rejectedImportService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -159,10 +162,8 @@ namespace NzbDrone.Core.Download
|
|||
{
|
||||
var firstResult = importResults.First();
|
||||
|
||||
if (firstResult.Result == ImportResultType.Rejected && firstResult.ImportDecision.LocalMovie == null)
|
||||
if (_rejectedImportService.Process(trackedDownload, firstResult))
|
||||
{
|
||||
trackedDownload.Warn(new TrackedDownloadStatusMessage(firstResult.Errors.First(), new List<string>()));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,14 +55,18 @@ namespace NzbDrone.Core.Download
|
|||
{
|
||||
try
|
||||
{
|
||||
// Process completed items followed by failed, this allows failed imports to have
|
||||
// their state changed and be processed immediately instead of the next execution.
|
||||
|
||||
if (enableCompletedDownloadHandling && trackedDownload.State == TrackedDownloadState.ImportPending)
|
||||
{
|
||||
_completedDownloadService.Import(trackedDownload);
|
||||
}
|
||||
|
||||
if (trackedDownload.State == TrackedDownloadState.FailedPending)
|
||||
{
|
||||
_failedDownloadService.ProcessFailed(trackedDownload);
|
||||
}
|
||||
else if (enableCompletedDownloadHandling && trackedDownload.State == TrackedDownloadState.ImportPending)
|
||||
{
|
||||
_completedDownloadService.Import(trackedDownload);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
60
src/NzbDrone.Core/Download/RejectedImportService.cs
Normal file
60
src/NzbDrone.Core/Download/RejectedImportService.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport;
|
||||
|
||||
namespace NzbDrone.Core.Download;
|
||||
|
||||
public interface IRejectedImportService
|
||||
{
|
||||
bool Process(TrackedDownload trackedDownload, ImportResult importResult);
|
||||
}
|
||||
|
||||
public class RejectedImportService : IRejectedImportService
|
||||
{
|
||||
private readonly ICachedIndexerSettingsProvider _cachedIndexerSettingsProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public RejectedImportService(ICachedIndexerSettingsProvider cachedIndexerSettingsProvider, Logger logger)
|
||||
{
|
||||
_cachedIndexerSettingsProvider = cachedIndexerSettingsProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool Process(TrackedDownload trackedDownload, ImportResult importResult)
|
||||
{
|
||||
if (importResult.Result != ImportResultType.Rejected || trackedDownload.RemoteMovie?.Release == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var indexerSettings = _cachedIndexerSettingsProvider.GetSettings(trackedDownload.RemoteMovie.Release.IndexerId);
|
||||
var rejectionReason = importResult.ImportDecision.Rejections.FirstOrDefault()?.Reason;
|
||||
|
||||
if (indexerSettings == null)
|
||||
{
|
||||
trackedDownload.Warn(new TrackedDownloadStatusMessage(trackedDownload.DownloadItem.Title, importResult.Errors));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rejectionReason == ImportRejectionReason.DangerousFile &&
|
||||
indexerSettings.FailDownloads.Contains(FailDownloads.PotentiallyDangerous))
|
||||
{
|
||||
_logger.Trace("Download '{0}' contains potentially dangerous file, marking as failed", trackedDownload.DownloadItem.Title);
|
||||
trackedDownload.Fail();
|
||||
}
|
||||
else if (rejectionReason == ImportRejectionReason.ExecutableFile &&
|
||||
indexerSettings.FailDownloads.Contains(FailDownloads.Executables))
|
||||
{
|
||||
_logger.Trace("Download '{0}' contains executable file, marking as failed", trackedDownload.DownloadItem.Title);
|
||||
trackedDownload.Fail();
|
||||
}
|
||||
else
|
||||
{
|
||||
trackedDownload.Warn(new TrackedDownloadStatusMessage(trackedDownload.DownloadItem.Title, importResult.Errors));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -35,6 +35,12 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
Status = TrackedDownloadStatus.Warning;
|
||||
StatusMessages = statusMessages;
|
||||
}
|
||||
|
||||
public void Fail()
|
||||
{
|
||||
Status = TrackedDownloadStatus.Error;
|
||||
State = TrackedDownloadState.FailedPending;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TrackedDownloadState
|
||||
|
|
|
@ -166,6 +166,11 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
{
|
||||
trackedDownload.RemoteMovie.Release.IndexerFlags = flags;
|
||||
}
|
||||
|
||||
if (downloadHistory != null)
|
||||
{
|
||||
trackedDownload.RemoteMovie.Release.IndexerId = downloadHistory.IndexerId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
69
src/NzbDrone.Core/Indexers/CachedIndexerSettingsProvider.cs
Normal file
69
src/NzbDrone.Core/Indexers/CachedIndexerSettingsProvider.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
namespace NzbDrone.Core.Indexers;
|
||||
|
||||
public interface ICachedIndexerSettingsProvider
|
||||
{
|
||||
CachedIndexerSettings GetSettings(int indexerId);
|
||||
}
|
||||
|
||||
public class CachedIndexerSettingsProvider : ICachedIndexerSettingsProvider, IHandle<ProviderUpdatedEvent<IIndexer>>
|
||||
{
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
private readonly ICached<CachedIndexerSettings> _cache;
|
||||
|
||||
public CachedIndexerSettingsProvider(IIndexerFactory indexerFactory, ICacheManager cacheManager)
|
||||
{
|
||||
_indexerFactory = indexerFactory;
|
||||
_cache = cacheManager.GetRollingCache<CachedIndexerSettings>(GetType(), "settingsByIndexer", TimeSpan.FromHours(1));
|
||||
}
|
||||
|
||||
public CachedIndexerSettings GetSettings(int indexerId)
|
||||
{
|
||||
if (indexerId == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _cache.Get(indexerId.ToString(), () => FetchIndexerSettings(indexerId));
|
||||
}
|
||||
|
||||
private CachedIndexerSettings FetchIndexerSettings(int indexerId)
|
||||
{
|
||||
var indexer = _indexerFactory.Get(indexerId);
|
||||
var indexerSettings = indexer.Settings as IIndexerSettings;
|
||||
|
||||
if (indexerSettings == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var settings = new CachedIndexerSettings
|
||||
{
|
||||
FailDownloads = indexerSettings.FailDownloads.Select(f => (FailDownloads)f).ToHashSet()
|
||||
};
|
||||
|
||||
if (indexer.Settings is ITorrentIndexerSettings torrentIndexerSettings)
|
||||
{
|
||||
settings.SeedCriteriaSettings = torrentIndexerSettings.SeedCriteria;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public void Handle(ProviderUpdatedEvent<IIndexer> message)
|
||||
{
|
||||
_cache.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public class CachedIndexerSettings
|
||||
{
|
||||
public HashSet<FailDownloads> FailDownloads { get; set; }
|
||||
public SeedCriteriaSettings SeedCriteriaSettings { get; set; }
|
||||
}
|
12
src/NzbDrone.Core/Indexers/FailDownloads.cs
Normal file
12
src/NzbDrone.Core/Indexers/FailDownloads.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.Indexers;
|
||||
|
||||
public enum FailDownloads
|
||||
{
|
||||
[FieldOption(Label = "Executables")]
|
||||
Executables = 0,
|
||||
|
||||
[FieldOption(Label = "Potentially Dangerous")]
|
||||
PotentiallyDangerous = 1
|
||||
}
|
|
@ -38,6 +38,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
|||
};
|
||||
|
||||
MultiLanguages = Array.Empty<int>();
|
||||
FailDownloads = Array.Empty<int>();
|
||||
RequiredFlags = Array.Empty<int>();
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,10 @@ namespace NzbDrone.Core.Indexers.FileList
|
|||
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
|
||||
public IEnumerable<int> FailDownloads { get; set; }
|
||||
|
||||
[FieldDefinition(9, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
public IEnumerable<int> RequiredFlags { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace NzbDrone.Core.Indexers.HDBits
|
|||
Codecs = Array.Empty<int>();
|
||||
Mediums = Array.Empty<int>();
|
||||
MultiLanguages = Array.Empty<int>();
|
||||
FailDownloads = Array.Empty<int>();
|
||||
RequiredFlags = Array.Empty<int>();
|
||||
}
|
||||
|
||||
|
@ -66,7 +67,10 @@ namespace NzbDrone.Core.Indexers.HDBits
|
|||
[FieldDefinition(9, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
[FieldDefinition(10, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
[FieldDefinition(10, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
|
||||
public IEnumerable<int> FailDownloads { get; set; }
|
||||
|
||||
[FieldDefinition(11, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
public IEnumerable<int> RequiredFlags { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -9,5 +9,7 @@ namespace NzbDrone.Core.Indexers
|
|||
|
||||
// TODO: Need to Create UI field for this and turn functionality back on per indexer.
|
||||
IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
IEnumerable<int> FailDownloads { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace NzbDrone.Core.Indexers.IPTorrents
|
|||
BaseUrl = string.Empty;
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
MultiLanguages = Array.Empty<int>();
|
||||
FailDownloads = Array.Empty<int>();
|
||||
RequiredFlags = Array.Empty<int>();
|
||||
}
|
||||
|
||||
|
@ -54,7 +55,10 @@ namespace NzbDrone.Core.Indexers.IPTorrents
|
|||
[FieldDefinition(4, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
|
||||
public IEnumerable<int> FailDownloads { get; set; }
|
||||
|
||||
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
public IEnumerable<int> RequiredFlags { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
ApiPath = "/api";
|
||||
Categories = new[] { 2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060 };
|
||||
MultiLanguages = Array.Empty<int>();
|
||||
FailDownloads = Array.Empty<int>();
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "URL")]
|
||||
|
@ -79,7 +80,10 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
[FieldDefinition(6, Type = FieldType.Checkbox, Label = "IndexerSettingsRemoveYear", HelpText = "IndexerSettingsRemoveYearHelpText", Advanced = true)]
|
||||
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
|
||||
public IEnumerable<int> FailDownloads { get; set; }
|
||||
|
||||
[FieldDefinition(7, Type = FieldType.Checkbox, Label = "IndexerSettingsRemoveYear", HelpText = "IndexerSettingsRemoveYearHelpText", Advanced = true)]
|
||||
public bool RemoveYear { get; set; }
|
||||
|
||||
// Field 8 is used by TorznabSettings MinimumSeeders
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace NzbDrone.Core.Indexers.Nyaa
|
|||
AdditionalParameters = "&cats=1_0&filter=1";
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
MultiLanguages = Array.Empty<int>();
|
||||
FailDownloads = Array.Empty<int>();
|
||||
RequiredFlags = Array.Empty<int>();
|
||||
}
|
||||
|
||||
|
@ -52,7 +53,10 @@ namespace NzbDrone.Core.Indexers.Nyaa
|
|||
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
|
||||
public IEnumerable<int> FailDownloads { get; set; }
|
||||
|
||||
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
public IEnumerable<int> RequiredFlags { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
|||
BaseUrl = "https://passthepopcorn.me";
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
MultiLanguages = Array.Empty<int>();
|
||||
FailDownloads = Array.Empty<int>();
|
||||
RequiredFlags = Array.Empty<int>();
|
||||
}
|
||||
|
||||
|
@ -54,7 +55,10 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
|||
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
|
||||
public IEnumerable<int> FailDownloads { get; set; }
|
||||
|
||||
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
public IEnumerable<int> RequiredFlags { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
using System;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
|
@ -14,15 +10,13 @@ namespace NzbDrone.Core.Indexers
|
|||
TorrentSeedConfiguration GetSeedConfiguration(int indexerId);
|
||||
}
|
||||
|
||||
public class SeedConfigProvider : ISeedConfigProvider, IHandle<ProviderUpdatedEvent<IIndexer>>
|
||||
public class SeedConfigProvider : ISeedConfigProvider
|
||||
{
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
private readonly ICached<SeedCriteriaSettings> _cache;
|
||||
private readonly ICachedIndexerSettingsProvider _cachedIndexerSettingsProvider;
|
||||
|
||||
public SeedConfigProvider(IIndexerFactory indexerFactory, ICacheManager cacheManager)
|
||||
public SeedConfigProvider(ICachedIndexerSettingsProvider cachedIndexerSettingsProvider)
|
||||
{
|
||||
_indexerFactory = indexerFactory;
|
||||
_cache = cacheManager.GetRollingCache<SeedCriteriaSettings>(GetType(), "criteriaByIndexer", TimeSpan.FromHours(1));
|
||||
_cachedIndexerSettingsProvider = cachedIndexerSettingsProvider;
|
||||
}
|
||||
|
||||
public TorrentSeedConfiguration GetSeedConfiguration(RemoteMovie remoteMovie)
|
||||
|
@ -47,7 +41,8 @@ namespace NzbDrone.Core.Indexers
|
|||
return null;
|
||||
}
|
||||
|
||||
var seedCriteria = _cache.Get(indexerId.ToString(), () => FetchSeedCriteria(indexerId));
|
||||
var settings = _cachedIndexerSettingsProvider.GetSettings(indexerId);
|
||||
var seedCriteria = settings?.SeedCriteriaSettings;
|
||||
|
||||
if (seedCriteria == null)
|
||||
{
|
||||
|
@ -68,25 +63,5 @@ namespace NzbDrone.Core.Indexers
|
|||
|
||||
return seedConfig;
|
||||
}
|
||||
|
||||
private SeedCriteriaSettings FetchSeedCriteria(int indexerId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var indexer = _indexerFactory.Get(indexerId);
|
||||
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
|
||||
|
||||
return torrentIndexerSettings?.SeedCriteria;
|
||||
}
|
||||
catch (ModelNotFoundException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(ProviderUpdatedEvent<IIndexer> message)
|
||||
{
|
||||
_cache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
|
|||
BaseUrl = "http://127.0.0.1";
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
MultiLanguages = Array.Empty<int>();
|
||||
FailDownloads = Array.Empty<int>();
|
||||
RequiredFlags = Array.Empty<int>();
|
||||
}
|
||||
|
||||
|
@ -52,7 +53,10 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
|
|||
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
|
||||
public IEnumerable<int> FailDownloads { get; set; }
|
||||
|
||||
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
public IEnumerable<int> RequiredFlags { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace NzbDrone.Core.Indexers.TorrentRss
|
|||
AllowZeroSize = false;
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
MultiLanguages = Array.Empty<int>();
|
||||
FailDownloads = Array.Empty<int>();
|
||||
RequiredFlags = Array.Empty<int>();
|
||||
}
|
||||
|
||||
|
@ -53,7 +54,10 @@ namespace NzbDrone.Core.Indexers.TorrentRss
|
|||
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(FailDownloads), Label = "IndexerSettingsFailDownloads", HelpText = "IndexerSettingsFailDownloadsHelpText", Advanced = true)]
|
||||
public IEnumerable<int> FailDownloads { get; set; }
|
||||
|
||||
[FieldDefinition(8, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
public IEnumerable<int> RequiredFlags { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -55,16 +55,16 @@ namespace NzbDrone.Core.Indexers.Torznab
|
|||
RequiredFlags = Array.Empty<int>();
|
||||
}
|
||||
|
||||
[FieldDefinition(8, Type = FieldType.Number, Label = "IndexerSettingsMinimumSeeders", HelpText = "IndexerSettingsMinimumSeedersHelpText", Advanced = true)]
|
||||
[FieldDefinition(9, Type = FieldType.Number, Label = "IndexerSettingsMinimumSeeders", HelpText = "IndexerSettingsMinimumSeedersHelpText", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
[FieldDefinition(9)]
|
||||
[FieldDefinition(10)]
|
||||
public SeedCriteriaSettings SeedCriteria { get; set; } = new ();
|
||||
|
||||
[FieldDefinition(10, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
|
||||
[FieldDefinition(11, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
|
||||
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
|
||||
|
||||
[FieldDefinition(11, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
[FieldDefinition(12, Type = FieldType.Select, SelectOptions = typeof(IndexerFlags), Label = "IndexerSettingsRequiredFlags", HelpText = "IndexerSettingsRequiredFlagsHelpText", HelpLink = "https://wiki.servarr.com/radarr/settings#indexer-flags", Advanced = true)]
|
||||
public IEnumerable<int> RequiredFlags { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -889,6 +889,8 @@
|
|||
"IndexerSettingsCategories": "Categories",
|
||||
"IndexerSettingsCookie": "Cookie",
|
||||
"IndexerSettingsCookieHelpText": "If your site requires a login cookie to access the RSS, you'll have to retrieve it via a browser.",
|
||||
"IndexerSettingsFailDownloads": "Fail Downloads",
|
||||
"IndexerSettingsFailDownloadsHelpText": "While processing completed downloads {appName} will treat selected errors preventing importing as failed downloads.",
|
||||
"IndexerSettingsMinimumSeeders": "Minimum Seeders",
|
||||
"IndexerSettingsMinimumSeedersHelpText": "Minimum number of seeders required.",
|
||||
"IndexerSettingsMultiLanguageRelease": "Multi Languages",
|
||||
|
|
|
@ -277,6 +277,26 @@ namespace NzbDrone.Core.MediaFiles
|
|||
|
||||
var extension = Path.GetExtension(fileInfo.Name);
|
||||
|
||||
if (FileExtensions.DangerousExtensions.Contains(extension))
|
||||
{
|
||||
return new List<ImportResult>
|
||||
{
|
||||
new ImportResult(new ImportDecision(new LocalMovie { Path = fileInfo.FullName },
|
||||
new ImportRejection(ImportRejectionReason.DangerousFile, $"Caution: Found potentially dangerous file with extension: {extension}")),
|
||||
$"Caution: Found potentially dangerous file with extension: {extension}")
|
||||
};
|
||||
}
|
||||
|
||||
if (FileExtensions.ExecutableExtensions.Contains(extension))
|
||||
{
|
||||
return new List<ImportResult>
|
||||
{
|
||||
new ImportResult(new ImportDecision(new LocalMovie { Path = fileInfo.FullName },
|
||||
new ImportRejection(ImportRejectionReason.ExecutableFile, $"Caution: Found executable file with extension: '{extension}'")),
|
||||
$"Caution: Found executable file with extension: '{extension}'")
|
||||
};
|
||||
}
|
||||
|
||||
if (extension.IsNullOrWhiteSpace() || !MediaFileExtensions.Extensions.Contains(extension))
|
||||
{
|
||||
_logger.Debug("[{0}] has an unsupported extension: '{1}'", fileInfo.FullName, extension);
|
||||
|
@ -335,6 +355,11 @@ namespace NzbDrone.Core.MediaFiles
|
|||
{
|
||||
var files = _diskProvider.GetFiles(folder, true);
|
||||
|
||||
if (files.Any(file => FileExtensions.DangerousExtensions.Contains(Path.GetExtension(file))))
|
||||
{
|
||||
return RejectionResult(ImportRejectionReason.DangerousFile, "Caution: Found potentially dangerous file");
|
||||
}
|
||||
|
||||
if (files.Any(file => FileExtensions.ExecutableExtensions.Contains(Path.GetExtension(file))))
|
||||
{
|
||||
return RejectionResult(ImportRejectionReason.ExecutableFile, "Caution: Found executable file");
|
||||
|
|
|
@ -18,19 +18,30 @@ namespace NzbDrone.Core.MediaFiles
|
|||
".tb2",
|
||||
".tbz2",
|
||||
".tgz",
|
||||
".zip",
|
||||
".zip"
|
||||
};
|
||||
|
||||
private static List<string> _dangerousExtensions = new List<string>
|
||||
{
|
||||
".arj",
|
||||
".lnk",
|
||||
".lzh",
|
||||
".ps1",
|
||||
".scr",
|
||||
".vbs",
|
||||
".zipx"
|
||||
};
|
||||
|
||||
private static List<string> _executableExtensions = new List<string>
|
||||
{
|
||||
".exe",
|
||||
".bat",
|
||||
".cmd",
|
||||
".exe",
|
||||
".sh"
|
||||
};
|
||||
|
||||
public static HashSet<string> ArchiveExtensions => new HashSet<string>(_archiveExtensions, StringComparer.OrdinalIgnoreCase);
|
||||
public static HashSet<string> DangerousExtensions => new HashSet<string>(_dangerousExtensions, StringComparer.OrdinalIgnoreCase);
|
||||
public static HashSet<string> ExecutableExtensions => new HashSet<string>(_executableExtensions, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ public enum ImportRejectionReason
|
|||
Unknown,
|
||||
FileLocked,
|
||||
UnknownMovie,
|
||||
DangerousFile,
|
||||
ExecutableFile,
|
||||
ArchiveFile,
|
||||
MovieFolder,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue