Added: Option to require indexer flags per indexer. (e.g. only download freeleech torrents from a private tracker) (#2460)

This commit is contained in:
Leonardo Galli 2018-02-01 14:18:52 +01:00 committed by GitHub
parent 78e5fdf3bc
commit 861962d06c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 193 additions and 120 deletions

View File

@ -41,6 +41,16 @@ namespace NzbDrone.Api.ClientSchema
};
var value = propertyInfo.GetValue(model, null);
if (propertyInfo.PropertyType.HasAttribute<FlagsAttribute>())
{
int intVal = (int)value;
value = Enum.GetValues(propertyInfo.PropertyType)
.Cast<int>()
.Where(f=> (f & intVal) == f)
.ToList();
}
if (value != null)
{
field.Value = value;
@ -131,6 +141,12 @@ namespace NzbDrone.Api.ClientSchema
propertyInfo.SetValue(target, value, null);
}
else if (propertyInfo.PropertyType.HasAttribute<FlagsAttribute>())
{
int value = field.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => Convert.ToInt32(s)).Sum();
propertyInfo.SetValue(target, value, null);
}
else
{

View File

@ -0,0 +1,110 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class RequiredIndexerFlagsSpecification : IDecisionEngineSpecification
{
private readonly IIndexerFactory _indexerFactory;
private readonly Logger _logger;
public RequiredIndexerFlagsSpecification(IIndexerFactory indexerFactory, Logger logger)
{
_indexerFactory = indexerFactory;
_logger = logger;
}
//public SpecificationPriority Priority => SpecificationPriority.Default;
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
if (torrentInfo == null || torrentInfo.IndexerId == 0)
{
return Decision.Accept();
}
IndexerDefinition indexer;
try
{
indexer = _indexerFactory.Get(torrentInfo.IndexerId);
}
catch (ModelNotFoundException)
{
_logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId);
return Decision.Accept();
}
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
if (torrentIndexerSettings != null)
{
var minimumSeeders = torrentIndexerSettings.MinimumSeeders;
if (torrentInfo.Seeders.HasValue && torrentInfo.Seeders.Value < minimumSeeders)
{
_logger.Debug("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
return Decision.Reject("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
}
}
return Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release;
if (torrentInfo == null || torrentInfo.IndexerId == 0)
{
return Decision.Accept();
}
IndexerDefinition indexer;
try
{
indexer = _indexerFactory.Get(torrentInfo.IndexerId);
}
catch (ModelNotFoundException)
{
_logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId);
return Decision.Accept();
}
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
if (torrentIndexerSettings != null)
{
var requiredFlags = torrentIndexerSettings.RequiredFlags;
var requiredFlag = (IndexerFlags) 0;
if (requiredFlags.Count() == 0)
{
return Decision.Accept();
}
foreach (var flag in requiredFlags)
{
if (torrentInfo.IndexerFlags.HasFlag((IndexerFlags)flag))
{
return Decision.Accept();
}
requiredFlag |= (IndexerFlags)flag;
}
_logger.Debug("None of the required indexer flags {0} where found. Found flags: {1}", requiredFlag, torrentInfo.IndexerFlags);
return Decision.Reject("None of the required indexer flags {0} where found. Found flags: {1}", requiredFlag, torrentInfo.IndexerFlags);
}
return Decision.Accept();
}
}
}

View File

@ -27,19 +27,13 @@ namespace NzbDrone.Core.Indexers.AwesomeHD
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
{
var onlyInternal = "";
if (Settings.Internal)
{
onlyInternal = "&internal=true";
}
if (searchParameters != null)
{
yield return new IndexerRequest($"{Settings.BaseUrl.Trim().TrimEnd('/')}/searchapi.php?action=imdbsearch&passkey={Settings.Passkey.Trim()}&imdb={searchParameters}", HttpAccept.Rss);
}
else
{
yield return new IndexerRequest($"{Settings.BaseUrl.Trim().TrimEnd('/')}/searchapi.php?action=latestmovies&passkey={Settings.Passkey.Trim()}{onlyInternal}", HttpAccept.Rss);
yield return new IndexerRequest($"{Settings.BaseUrl.Trim().TrimEnd('/')}/searchapi.php?action=latestmovies&passkey={Settings.Passkey.Trim()}", HttpAccept.Rss);
}
}

View File

@ -1,5 +1,7 @@
using FluentValidation;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
@ -30,11 +32,11 @@ namespace NzbDrone.Core.Indexers.AwesomeHD
[FieldDefinition(1, Label = "Passkey")]
public string Passkey { get; set; }
[FieldDefinition(2, Type = FieldType.Checkbox, Label = "Require Internal", HelpText = "Will only include internal releases for RSS Sync.")]
public bool Internal { get; set; }
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
[FieldDefinition(2, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(3, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()
{

View File

@ -82,15 +82,6 @@ namespace NzbDrone.Core.Indexers.HDBits
});
}
// order by internal
if (_settings.PreferInternal)
{
return
torrentInfos.OrderByDescending(o => o.PublishDate)
.ThenBy(o => ((dynamic)o).Internal ? 0 : 1)
.ToArray();
}
return torrentInfos.ToArray();
}

View File

@ -63,12 +63,6 @@ namespace NzbDrone.Core.Indexers.HDBits
query.Codec = Settings.Codecs.ToArray();
query.Medium = Settings.Mediums.ToArray();
// Require Internal only if came from RSS sync
if (Settings.RequireInternal && query.ImdbInfo == null)
{
query.Origin = 1;
}
request.SetContent(query.ToJson());
yield return new IndexerRequest(request);

View File

@ -6,6 +6,7 @@ using NzbDrone.Core.Validation;
using System.Linq.Expressions;
using FluentValidation.Results;
using System.Collections.Generic;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.HDBits
{
@ -41,23 +42,20 @@ namespace NzbDrone.Core.Indexers.HDBits
[FieldDefinition(2, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your API key will be sent to that host.")]
public string BaseUrl { get; set; }
[FieldDefinition(3, Label = "Prefer Internal", Type = FieldType.Checkbox, HelpText = "Favors Internal releases over all other releases.")]
public bool PreferInternal { get; set; }
[FieldDefinition(4, Label = "Require Internal", Type = FieldType.Checkbox, HelpText = "Require Internal releases for release to be accepted.")]
public bool RequireInternal { get; set; }
[FieldDefinition(5, Label = "Categories", Type = FieldType.Tag, SelectOptions = typeof(HdBitsCategory), Advanced = true, HelpText = "Options: Movie, TV, Documentary, Music, Sport, Audio, XXX, MiscDemo. If unspecified, all options are used.")]
[FieldDefinition(3, Label = "Categories", Type = FieldType.Tag, SelectOptions = typeof(HdBitsCategory), Advanced = true, HelpText = "Options: Movie, TV, Documentary, Music, Sport, Audio, XXX, MiscDemo. If unspecified, all options are used.")]
public IEnumerable<int> Categories { get; set; }
[FieldDefinition(6, Label = "Codecs", Type = FieldType.Tag, SelectOptions = typeof(HdBitsCodec), Advanced = true, HelpText = "Options: h264, Mpeg2, VC1, Xvid. If unspecified, all options are used.")]
[FieldDefinition(4, Label = "Codecs", Type = FieldType.Tag, SelectOptions = typeof(HdBitsCodec), Advanced = true, HelpText = "Options: h264, Mpeg2, VC1, Xvid. If unspecified, all options are used.")]
public IEnumerable<int> Codecs { get; set; }
[FieldDefinition(7, Label = "Mediums", Type = FieldType.Tag, SelectOptions = typeof(HdBitsMedium), Advanced = true, HelpText = "Options: BluRay, Encode, Capture, Remux, WebDL. If unspecified, all options are used.")]
[FieldDefinition(5, Label = "Mediums", Type = FieldType.Tag, SelectOptions = typeof(HdBitsMedium), Advanced = true, HelpText = "Options: BluRay, Encode, Capture, Remux, WebDL. If unspecified, all options are used.")]
public IEnumerable<int> Mediums { get; set; }
[FieldDefinition(8, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
[FieldDefinition(6, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(7, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()
{

View File

@ -1,7 +1,9 @@
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
@ -36,6 +38,9 @@ namespace NzbDrone.Core.Indexers.IPTorrents
[FieldDefinition(1, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(2, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()
{

View File

@ -1,7 +1,10 @@
namespace NzbDrone.Core.Indexers
using System.Collections.Generic;
namespace NzbDrone.Core.Indexers
{
public interface ITorrentIndexerSettings : IIndexerSettings
{
int MinimumSeeders { get; set; }
IEnumerable<int> RequiredFlags { get; set; }
}
}

View File

@ -1,7 +1,10 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Validation;
using System.Text.RegularExpressions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Nyaa
{
public class NyaaSettingsValidator : AbstractValidator<NyaaSettings>
@ -32,6 +35,9 @@ namespace NzbDrone.Core.Indexers.Nyaa
[FieldDefinition(2, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(3, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()
{

View File

@ -73,8 +73,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
}
// Only add approved torrents
if (_settings.RequireApproved && torrent.Checked)
{
torrentInfos.Add(new PassThePopcornInfo()
{
Guid = string.Format("PassThePopcorn-{0}", id),
@ -91,67 +90,10 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
ImdbId = (result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0),
IndexerFlags = flags
});
}
// Add all torrents
else if (!_settings.RequireApproved)
{
torrentInfos.Add(new PassThePopcornInfo()
{
Guid = string.Format("PassThePopcorn-{0}", id),
Title = title,
Size = long.Parse(torrent.Size),
DownloadUrl = GetDownloadUrl(id, jsonResponse.AuthKey, jsonResponse.PassKey),
InfoUrl = GetInfoUrl(result.GroupId, id),
Seeders = int.Parse(torrent.Seeders),
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
PublishDate = torrent.UploadTime.ToUniversalTime(),
Golden = torrent.GoldenPopcorn,
Scene = torrent.Scene,
Approved = torrent.Checked,
ImdbId = (result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0),
IndexerFlags = flags
});
}
// Don't add any torrents
else if (_settings.RequireApproved && !torrent.Checked)
{
continue;
}
}
}
// prefer golden
if (_settings.Golden)
{
if (_settings.Scene)
{
return
torrentInfos.OrderByDescending(o => o.PublishDate)
.ThenBy(o => ((dynamic)o).Golden ? 0 : 1)
.ThenBy(o => ((dynamic)o).Scene ? 0 : 1)
.ToArray();
}
return
torrentInfos.OrderByDescending(o => o.PublishDate)
.ThenBy(o => ((dynamic)o).Golden ? 0 : 1)
.ToArray();
}
// prefer scene
if (_settings.Scene)
{
return
torrentInfos.OrderByDescending(o => o.PublishDate)
.ThenBy(o => ((dynamic)o).Scene ? 0 : 1)
.ToArray();
}
// order by date
return
torrentInfos
.OrderByDescending(o => o.PublishDate)
.ToArray();
torrentInfos;
}

View File

@ -40,10 +40,8 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
var filter = "";
if (searchParameters == null)
{
if (Settings.RequireGolden)
{
filter = "&scene=2";
}
}
var request =

View File

@ -1,8 +1,10 @@
using FluentValidation;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
using System.Text.RegularExpressions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.PassThePopcorn
{
@ -39,20 +41,11 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
[FieldDefinition(3, Label = "Passkey", HelpText = "PTP Passkey")]
public string Passkey { get; set; }
[FieldDefinition(4, Label = "Prefer Golden", Type = FieldType.Checkbox , HelpText = "Favors Golden Popcorn-releases over all other releases.")]
public bool Golden { get; set; }
[FieldDefinition(5, Label = "Prefer Scene", Type = FieldType.Checkbox, HelpText = "Favors scene-releases over non-scene releases.")]
public bool Scene { get; set; }
[FieldDefinition(6, Label = "Require Approved", Type = FieldType.Checkbox, HelpText = "Require staff-approval for releases to be accepted.")]
public bool RequireApproved { get; set; }
[FieldDefinition(7, Label = "Require Golden", Type = FieldType.Checkbox, HelpText = "Require Golden Popcorn-releases for releases to be accepted.")]
public bool RequireGolden { get; set; }
[FieldDefinition(8, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
[FieldDefinition(4, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(5, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()
{

View File

@ -1,5 +1,7 @@
using FluentValidation;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Rarbg
@ -34,6 +36,9 @@ namespace NzbDrone.Core.Indexers.Rarbg
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(4, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()
{

View File

@ -1,5 +1,7 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
@ -34,6 +36,9 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(4, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()
{

View File

@ -1,5 +1,7 @@
using FluentValidation;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.TorrentRss
@ -34,6 +36,9 @@ namespace NzbDrone.Core.Indexers.TorrentRss
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(4, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()
{

View File

@ -1,10 +1,12 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using FluentValidation;
using FluentValidation.Results;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Torznab
@ -58,6 +60,9 @@ namespace NzbDrone.Core.Indexers.Torznab
[FieldDefinition(7, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(8, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public override NzbDroneValidationResult Validate()
{

View File

@ -127,6 +127,7 @@
<Compile Include="Datastore\Migration\123_create_netimport_table.cs" />
<Compile Include="Datastore\Migration\140_add_alternative_titles_table.cs" />
<Compile Include="Datastore\Migration\141_fix_duplicate_alt_titles.cs" />
<Compile Include="DecisionEngine\Specifications\RequiredIndexerFlagsSpecification.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedAlternativeTitles.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\GrabbedReleaseQualitySpecification.cs" />
<Compile Include="MediaFiles\Events\MovieFileUpdatedEvent.cs" />