using System.Collections.Generic; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; namespace NzbDrone.Core.DecisionEngine.Specifications { public interface IUpgradableSpecification { bool IsUpgradable(Profile profile, QualityModel currentQuality, List currentCustomFormats, QualityModel newQuality, List newCustomFormats); bool CutoffNotMet(Profile profile, QualityModel currentQuality, List currentFormats, QualityModel newQuality = null); bool QualityCutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null); bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality); bool IsUpgradeAllowed(Profile qualityProfile, QualityModel currentQuality, List currentCustomFormats, QualityModel newQuality, List newCustomFormats); } public class UpgradableSpecification : IUpgradableSpecification { private readonly IConfigService _configService; private readonly Logger _logger; public UpgradableSpecification(IConfigService configService, Logger logger) { _configService = configService; _logger = logger; } public bool IsUpgradable(Profile profile, QualityModel currentQuality, List currentCustomFormats, QualityModel newQuality, List newCustomFormats) { var qualityComparer = new QualityModelComparer(profile); var qualityCompare = qualityComparer.Compare(newQuality?.Quality, currentQuality.Quality); var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; if (qualityCompare > 0) { _logger.Debug("New item has a better quality"); return true; } if (qualityCompare < 0) { _logger.Debug("Existing item has better quality, skipping"); return false; } var qualityRevisionCompare = newQuality?.Revision.CompareTo(currentQuality.Revision); // Accept unless the user doesn't want to prefer propers, optionally they can // use preferred words to prefer propers/repacks over non-propers/repacks. if (downloadPropersAndRepacks != ProperDownloadTypes.DoNotPrefer && qualityRevisionCompare > 0) { return true; } var currentFormatScore = profile.CalculateCustomFormatScore(currentCustomFormats); var newFormatScore = profile.CalculateCustomFormatScore(newCustomFormats); // Reject unless the user does not prefer propers/repacks and it's a revision downgrade. if (downloadPropersAndRepacks != ProperDownloadTypes.DoNotPrefer && qualityRevisionCompare < 0) { _logger.Debug("Existing item has a better quality revision, skipping"); return false; } if (newFormatScore <= currentFormatScore) { _logger.Debug("New item's custom formats [{0}] do not improve on [{1}], skipping", newCustomFormats.ConcatToString(), currentCustomFormats.ConcatToString()); return false; } _logger.Debug("New item has a custom format upgrade"); return true; } public bool QualityCutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null) { var cutoff = profile.UpgradeAllowed ? profile.Cutoff : profile.FirststAllowedQuality().Id; var cutoffCompare = new QualityModelComparer(profile).Compare(currentQuality.Quality.Id, cutoff); if (cutoffCompare < 0) { return true; } if (newQuality != null && IsRevisionUpgrade(currentQuality, newQuality)) { return true; } return false; } private bool CustomFormatCutoffNotMet(Profile profile, List currentFormats) { var score = profile.CalculateCustomFormatScore(currentFormats); return score < profile.CutoffFormatScore; } public bool CutoffNotMet(Profile profile, QualityModel currentQuality, List currentFormats, QualityModel newQuality = null) { if (QualityCutoffNotMet(profile, currentQuality, newQuality)) { return true; } if (CustomFormatCutoffNotMet(profile, currentFormats)) { return true; } _logger.Debug("Existing item meets cut-off. skipping."); return false; } public bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality) { var compare = newQuality.Revision.CompareTo(currentQuality.Revision); if (currentQuality.Quality == newQuality.Quality && compare > 0) { _logger.Debug("New quality is a better revision for existing quality"); return true; } return false; } public bool IsUpgradeAllowed(Profile qualityProfile, QualityModel currentQuality, List currentCustomFormats, QualityModel newQuality, List newCustomFormats) { var isQualityUpgrade = new QualityModelComparer(qualityProfile).Compare(newQuality, currentQuality) > 0; var isCustomFormatUpgrade = qualityProfile.CalculateCustomFormatScore(newCustomFormats) > qualityProfile.CalculateCustomFormatScore(currentCustomFormats); if ((isQualityUpgrade || isCustomFormatUpgrade) && qualityProfile.UpgradeAllowed) { _logger.Debug("Quality profile allows upgrading"); return true; } return false; } } }