Release scoring

This commit is contained in:
Mark McDowall
2016-04-08 13:23:09 -07:00
parent dc694b0f34
commit 8a6d1ef373
5 changed files with 319 additions and 32 deletions
@@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles.Delay;
namespace NzbDrone.Core.DecisionEngine
{
public class DownloadDecisionComparer : IComparer<DownloadDecision>
{
private readonly IDelayProfileService _delayProfileService;
public delegate int CompareDelegate(DownloadDecision x, DownloadDecision y);
public delegate int CompareDelegate<TSubject, TValue>(DownloadDecision x, DownloadDecision y);
public DownloadDecisionComparer(IDelayProfileService delayProfileService)
{
_delayProfileService = delayProfileService;
}
public int Compare(DownloadDecision x, DownloadDecision y)
{
var comparers = new List<CompareDelegate>
{
CompareQuality,
CompareProtocol,
CompareEpisodeCount,
CompareEpisodeNumber,
ComparePeersIfTorrent,
CompareAgeIfUsenet,
CompareSize
};
return comparers.Select(comparer => comparer(x, y)).FirstOrDefault(result => result != 0);
}
private int CompareBy<TSubject, TValue>(TSubject left, TSubject right, Func<TSubject, TValue> funcValue)
where TValue : IComparable<TValue>
{
var leftValue = funcValue(left);
var rightValue = funcValue(right);
return leftValue.CompareTo(rightValue);
}
private int CompareByReverse<TSubject, TValue>(TSubject left, TSubject right, Func<TSubject, TValue> funcValue)
where TValue : IComparable<TValue>
{
return CompareBy(left, right, funcValue)*-1;
}
private int CompareAll(params int[] comparers)
{
return comparers.Select(comparer => comparer).FirstOrDefault(result => result != 0);
}
private int CompareQuality(DownloadDecision x, DownloadDecision y)
{
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.Profile.Value.Items.FindIndex(v => v.Quality == remoteEpisode.ParsedEpisodeInfo.Quality.Quality)),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version));
}
private int CompareProtocol(DownloadDecision x, DownloadDecision y)
{
var result = CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
{
var delayProfile = _delayProfileService.BestForTags(remoteEpisode.Series.Tags);
var downloadProtocol = remoteEpisode.Release.DownloadProtocol;
return downloadProtocol == delayProfile.PreferredProtocol;
});
return result;
}
private int CompareEpisodeCount(DownloadDecision x, DownloadDecision y)
{
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.FullSeason),
CompareByReverse(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Episodes.Count));
}
private int CompareEpisodeNumber(DownloadDecision x, DownloadDecision y)
{
return CompareByReverse(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Episodes.Select(e => e.EpisodeNumber).MinOrDefault());
}
private int ComparePeersIfTorrent(DownloadDecision x, DownloadDecision y)
{
// Different protocols should get caught when checking the preferred protocol,
// since we're dealing with the same series in our comparisions
if (x.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Torrent ||
y.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Torrent)
{
return 0;
}
return CompareAll(
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
{
var seeders = TorrentInfo.GetSeeders(remoteEpisode.Release);
return seeders.HasValue && seeders.Value > 0 ? Math.Round(Math.Log10(seeders.Value)) : 0;
}),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
{
var peers = TorrentInfo.GetPeers(remoteEpisode.Release);
return peers.HasValue && peers.Value > 0 ? Math.Round(Math.Log10(peers.Value)) : 0;
}));
}
private int CompareAgeIfUsenet(DownloadDecision x, DownloadDecision y)
{
if (x.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Usenet ||
y.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Usenet)
{
return 0;
}
return CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
{
var ageHours = remoteEpisode.Release.AgeHours;
var age = remoteEpisode.Release.Age;
if (ageHours < 1)
{
return 1000;
}
if (ageHours <= 24)
{
return 100;
}
if (age <= 7)
{
return 10;
}
return 1;
});
}
private int CompareSize(DownloadDecision x, DownloadDecision y)
{
// TODO: Is smaller better? Smaller for usenet could mean no par2 files.
return CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Release.Size.Round(200.Megabytes()));
}
}
}
@@ -1,11 +1,6 @@
using System;
using System.Linq;
using System.Linq;
using System.Collections.Generic;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.DecisionEngine
{
@@ -26,35 +21,13 @@ namespace NzbDrone.Core.DecisionEngine
public List<DownloadDecision> PrioritizeDecisions(List<DownloadDecision> decisions)
{
return decisions.Where(c => c.RemoteEpisode.Series != null)
.GroupBy(c => c.RemoteEpisode.Series.Id, (seriesId, d) =>
.GroupBy(c => c.RemoteEpisode.Series.Id, (seriesId, downloadDecisions) =>
{
var downloadDecisions = d.ToList();
var series = downloadDecisions.First().RemoteEpisode.Series;
return downloadDecisions
.OrderByDescending(c => c.RemoteEpisode.ParsedEpisodeInfo.Quality, new QualityModelComparer(series.Profile))
.ThenBy(c => c.RemoteEpisode.Episodes.Select(e => e.EpisodeNumber).MinOrDefault())
.ThenBy(c => PrioritizeDownloadProtocol(series, c.RemoteEpisode.Release.DownloadProtocol))
.ThenByDescending(c => c.RemoteEpisode.Episodes.Count)
.ThenBy(c => c.RemoteEpisode.Release.Size.Round(200.Megabytes()) / Math.Max(1, c.RemoteEpisode.Episodes.Count))
.ThenByDescending(c => TorrentInfo.GetSeeders(c.RemoteEpisode.Release))
.ThenBy(c => c.RemoteEpisode.Release.Age);
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_delayProfileService));
})
.SelectMany(c => c)
.Union(decisions.Where(c => c.RemoteEpisode.Series == null))
.ToList();
}
private int PrioritizeDownloadProtocol(Series series, DownloadProtocol downloadProtocol)
{
var delayProfile = _delayProfileService.BestForTags(series.Tags);
if (downloadProtocol == delayProfile.PreferredProtocol)
{
return 0;
}
return 1;
}
}
}