public void equal_operand_false_proper() { var first = new QualityModel(QualityTypes.Bluray1080p, true); var second = new QualityModel(QualityTypes.Bluray1080p, false); (first == second).Should().BeFalse(); }
public void Icomparer_lesser_proper() { var first = new QualityModel(QualityTypes.DVD, false); var second = new QualityModel(QualityTypes.DVD, true); first.Should().BeLessThan(second); }
public void Icomparer_lesser() { var first = new QualityModel(Quality.DVD, true); var second = new QualityModel(Quality.Bluray1080p, true); first.Should().BeLessThan(second); }
public void Icomparer_greater_proper() { var first = new QualityModel(QualityTypes.Bluray1080p, false); var second = new QualityModel(QualityTypes.Bluray1080p, true); second.Should().BeGreaterThan(first); }
public void equal_operand_false() { var first = new QualityModel(Quality.Bluray1080p, true); var second = new QualityModel(Quality.Unknown, true); (first == second).Should().BeFalse(); }
public void not_equal_operand() { var first = new QualityModel(QualityTypes.Bluray1080p, true); var second = new QualityModel(QualityTypes.Bluray1080p, true); (first != second).Should().BeFalse(); }
public void Icomparer_greater_test() { var first = new QualityModel(Quality.DVD, true); var second = new QualityModel(Quality.Bluray1080p, true); second.Should().BeGreaterThan(first); }
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, bool sceneSource, QualityModel quality = null) { var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series.Id); _logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count()); return GetDecisions(newFiles, series, sceneSource, quality).ToList(); }
public void equal_operand() { var first = new QualityModel(QualityTypes.Bluray1080p, true); var second = new QualityModel(QualityTypes.Bluray1080p, true); (first == second).Should().BeTrue(); (first >= second).Should().BeTrue(); (first <= second).Should().BeTrue(); }
public bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality) { if (currentQuality.Quality == newQuality.Quality && newQuality > currentQuality) { _logger.Trace("New quality is a proper for existing quality"); return true; } return false; }
public void should_be_greater_when_first_quality_is_a_proper_for_the_same_quality() { GivenDefaultProfile(); var first = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)); var second = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)); var compare = Subject.Compare(first, second); compare.Should().BeGreaterThan(0); }
public void should_be_lesser_when_second_quality_is_greater_than_first() { GivenDefaultProfile(); var first = new QualityModel(Quality.DVD); var second = new QualityModel(Quality.Bluray1080p); var compare = Subject.Compare(first, second); compare.Should().BeLessThan(0); }
public void should_be_greater_when_using_a_custom_profile() { GivenCustomProfile(); var first = new QualityModel(Quality.DVD); var second = new QualityModel(Quality.Bluray720p); var compare = Subject.Compare(first, second); compare.Should().BeGreaterThan(0); }
public bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality) { var compare = newQuality.Revision.CompareTo(currentQuality.Revision); if (currentQuality.Quality == newQuality.Quality && compare > 0) { return true; } return false; }
public void Icomparer_greater_proper() { GivenDefaultQualityProfile(); var first = new QualityModel(Quality.Bluray1080p, false); var second = new QualityModel(Quality.Bluray1080p, true); var compare = Subject.Compare(second, first); compare.Should().BeGreaterThan(0); }
public void Icomparer_lesser_proper() { GivenDefaultQualityProfile(); var first = new QualityModel(Quality.DVD, false); var second = new QualityModel(Quality.DVD, true); var compare = Subject.Compare(first, second); compare.Should().BeLessThan(0); }
public void Icomparer_greater_custom_order() { GivenCustomQualityProfile(); var first = new QualityModel(Quality.DVD, true); var second = new QualityModel(Quality.Bluray720p, true); var compare = Subject.Compare(first, second); compare.Should().BeGreaterThan(0); }
public bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality) { int compare = newQuality.Proper.CompareTo(currentQuality.Proper); if (currentQuality.Quality == newQuality.Quality && compare > 0) { _logger.Trace("New quality is a proper for existing quality"); return true; } return false; }
public bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality) { int 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 IsSample(Series series, QualityModel quality, string path, long size, int seasonNumber) { if (seasonNumber == 0) { _logger.Debug("Special, skipping sample check"); return false; } var extension = Path.GetExtension(path); if (extension != null && extension.Equals(".flv", StringComparison.InvariantCultureIgnoreCase)) { _logger.Debug("Skipping sample check for .flv file"); return false; } if (extension != null && extension.Equals(".strm", StringComparison.InvariantCultureIgnoreCase)) { _logger.Debug("Skipping sample check for .strm file"); return false; } try { var runTime = _videoFileInfoReader.GetRunTime(path); var minimumRuntime = GetMinimumAllowedRuntime(series); if (runTime.TotalMinutes.Equals(0)) { _logger.Error("[{0}] has a runtime of 0, is it a valid video file?", path); return true; } if (runTime.TotalSeconds < minimumRuntime) { _logger.Debug("[{0}] appears to be a sample. Runtime: {1} seconds. Expected at least: {2} seconds", path, runTime, minimumRuntime); return true; } } catch (DllNotFoundException) { _logger.Debug("Falling back to file size detection"); return CheckSize(size, quality); } _logger.Debug("Runtime is over 90 seconds"); return false; }
public void embedded_document_as_json() { var quality = new QualityModel { Quality = Quality.Bluray720p, Revision = new Revision(version: 2 )}; var history = Builder<History.History>.CreateNew() .With(c => c.Id = 0) .With(c => c.Quality = quality) .Build(); Db.Insert(history); var loadedQuality = Db.Single<History.History>().Quality; loadedQuality.Should().Be(quality); }
public bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null) { if (currentQuality.Quality >= profile.Cutoff) { if (newQuality != null && IsProperUpgrade(currentQuality, newQuality)) { return true; } _logger.Trace("Existing item meets cut-off. skipping."); return false; } return true; }
public void OnGrab(Series series, RemoteEpisode episode, QualityModel quality, WebhookSettings settings) { var payload = new WebhookPayload { EventType = "Grab", Series = new WebhookSeries(series), Episodes = episode.Episodes.ConvertAll(x => new WebhookEpisode(x) { Quality = quality.Quality.Name, QualityVersion = quality.Revision.Version, ReleaseGroup = episode.ParsedEpisodeInfo.ReleaseGroup }) }; NotifyWebhook(payload, settings); }
public bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null) { var compare = new QualityModelComparer(profile).Compare(currentQuality.Quality, profile.Cutoff); if (compare < 0) { return true; } if (newQuality != null && IsRevisionUpgrade(currentQuality, newQuality)) { return true; } return false; }
public bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null) { int compare = new QualityModelComparer(profile).Compare(currentQuality.Quality, profile.Cutoff); if (compare >= 0) { if (newQuality != null && IsProperUpgrade(currentQuality, newQuality)) { return true; } _logger.Debug("Existing item meets cut-off. skipping."); return false; } return true; }
public bool IsUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null) { if (newQuality != null) { int compare = new QualityModelComparer(profile).Compare(newQuality, currentQuality); if (compare <= 0) { return false; } if (IsRevisionUpgrade(currentQuality, newQuality)) { return true; } } return true; }
public bool IsUpgradable(QualityModel currentQuality, QualityModel newQuality = null) { if (newQuality != null) { if (currentQuality >= newQuality) { _logger.Trace("existing item has better or equal quality. skipping"); return false; } if (IsProperUpgrade(currentQuality, newQuality)) { return true; } } return true; }
private IEnumerable<ImportDecision> GetDecisions(IEnumerable<String> videoFiles, Series series, bool sceneSource, QualityModel quality = null) { foreach (var file in videoFiles) { ImportDecision decision = null; try { var parsedEpisode = _parsingService.GetLocalEpisode(file, series, sceneSource); if (parsedEpisode != null) { if (quality != null && new QualityModelComparer(parsedEpisode.Series.QualityProfile).Compare(quality, parsedEpisode.Quality) > 0) { _logger.Trace("Using quality from folder: {0}", quality); parsedEpisode.Quality = quality; } parsedEpisode.Size = _diskProvider.GetFileSize(file); _logger.Trace("Size: {0}", parsedEpisode.Size); decision = GetDecision(parsedEpisode); } else { parsedEpisode = new LocalEpisode(); parsedEpisode.Path = file; decision = new ImportDecision(parsedEpisode, "Unable to parse file"); } } catch (Exception e) { _logger.ErrorException("Couldn't import file." + file, e); } if (decision != null) { yield return decision; } } }
public virtual void UpdateEpisodeStatus(int episodeId, EpisodeStatusType episodeStatus, QualityModel quality) { try { logger.Trace("Sending Status update to client. EpisodeId: {0}, Status: {1}", episodeId, episodeStatus); GetClients().updatedStatus(new { EpisodeId = episodeId, EpisodeStatus = episodeStatus.ToString(), Quality = (quality == null ? String.Empty : quality.Quality.ToString()) }); } catch (Exception ex) { logger.TraceException("Error", ex); throw; } }
public bool IsUpgradable(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null) { if (newQuality != null) { int compare = new QualityModelComparer(profile).Compare(newQuality, currentQuality); if (compare <= 0) { _logger.Trace("existing item has better or equal quality. skipping"); return false; } if (IsProperUpgrade(currentQuality, newQuality)) { return true; } } return true; }
private bool UseFolderQuality(ParsedEpisodeInfo folderInfo, QualityModel fileQuality, Series series) { if (folderInfo == null) { return(false); } if (folderInfo.Quality.Quality == Quality.Unknown) { return(false); } if (fileQuality.QualitySource == QualitySource.Extension) { return(true); } if (new QualityModelComparer(series.Profile).Compare(folderInfo.Quality, fileQuality) > 0) { return(true); } return(false); }
public ManualImportItem ReprocessItem(string path, string downloadId, int movieId, QualityModel quality, List <Language> languages) { var rootFolder = Path.GetDirectoryName(path); var movie = _movieService.GetMovie(movieId); var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem; var localEpisode = new LocalMovie { Movie = movie, FileMovieInfo = Parser.Parser.ParseMoviePath(path), DownloadClientMovieInfo = downloadClientItem == null ? null : Parser.Parser.ParseMovieTitle(downloadClientItem.Title), Path = path, SceneSource = SceneSource(movie, rootFolder), ExistingFile = movie.Path.IsParentPath(path), Size = _diskProvider.GetFileSize(path), Languages = (languages?.SingleOrDefault() ?? Language.Unknown) == Language.Unknown ? LanguageParser.ParseLanguages(path) : languages, Quality = quality.Quality == Quality.Unknown ? QualityParser.ParseQuality(path) : quality }; return(MapItem(_importDecisionMaker.GetDecision(localEpisode, downloadClientItem), rootFolder, downloadId, null)); }
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); }
public int CreateQuality(QualityModel quality) { return(new SaveQualityOperation(_repository).Execute(quality)); }
public virtual void UpdateEpisodeStatus(int episodeId, EpisodeStatusType episodeStatus, QualityModel quality) { try { logger.Trace("Sending Status update to client. EpisodeId: {0}, Status: {1}", episodeId, episodeStatus); var context = GlobalHost.ConnectionManager.GetHubContext <EpisodeHub>(); context.Clients.updatedStatus(new { EpisodeId = episodeId, EpisodeStatus = episodeStatus.ToString(), Quality = (quality == null ? String.Empty : quality.Quality.ToString()) }); } catch (Exception ex) { logger.TraceException("Error", ex); throw; } }
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { //How do we want to handle drone being off and the automatic search being triggered? //TODO: Add a flag to the search to state it is a "scheduled" search if (searchCriteria != null) { _logger.Debug("Ignore delay for searches"); return(Decision.Accept()); } var profile = subject.Series.Profile.Value; var delayProfile = _delayProfileService.BestForTags(subject.Series.Tags); var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol); var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol; if (delay == 0) { _logger.Debug("Profile does not require a waiting period before download for {0}.", subject.Release.DownloadProtocol); return(Decision.Accept()); } var comparer = new QualityModelComparer(profile); if (isPreferredProtocol) { foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) { var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, file.Quality, subject.ParsedEpisodeInfo.Quality); if (upgradable) { var revisionUpgrade = _qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality); if (revisionUpgrade) { _logger.Debug("New quality is a better revision for existing quality, skipping delay"); return(Decision.Accept()); } } } } //If quality meets or exceeds the best allowed quality in the profile accept it immediately var bestQualityInProfile = new QualityModel(profile.LastAllowedQuality()); var isBestInProfile = comparer.Compare(subject.ParsedEpisodeInfo.Quality, bestQualityInProfile) >= 0; if (isBestInProfile && isPreferredProtocol) { _logger.Debug("Quality is highest in profile for preferred protocol, will not delay"); return(Decision.Accept()); } var episodeIds = subject.Episodes.Select(e => e.Id); var oldest = _pendingReleaseService.OldestPendingRelease(subject.Series.Id, episodeIds); if (oldest != null && oldest.Release.AgeMinutes > delay) { return(Decision.Accept()); } if (subject.Release.AgeMinutes < delay) { _logger.Debug("Waiting for better quality release, There is a {0} minute delay on {1}", delay, subject.Release.DownloadProtocol); return(Decision.Reject("Waiting for better quality release")); } return(Decision.Accept()); }
public bool IsUpgradeAllowed(QualityProfile qualityProfile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, QualityModel newQuality, Language newLanguage) { var isQualityUpgrade = new QualityModelComparer(qualityProfile).Compare(newQuality, currentQuality) > 0; var isLanguageUpgrade = new LanguageComparer(languageProfile).Compare(newLanguage, currentLanguage) > 0; if (isQualityUpgrade && qualityProfile.UpgradeAllowed || isLanguageUpgrade && languageProfile.UpgradeAllowed) { _logger.Debug("At least one profile allows upgrading"); return(true); } if (isQualityUpgrade && !qualityProfile.UpgradeAllowed) { _logger.Debug("Quality profile does not allow upgrades, skipping"); return(false); } if (isLanguageUpgrade && !languageProfile.UpgradeAllowed) { _logger.Debug("Language profile does not allow upgrades, skipping"); return(false); } return(true); }
public bool IsUpgradeAllowed(QualityProfile qualityProfile, List <QualityModel> currentQualities, QualityModel newQuality) { var isQualityUpgrade = IsQualityUpgradable(qualityProfile, currentQualities, newQuality); return(CheckUpgradeAllowed(qualityProfile, isQualityUpgrade)); }
public ManualImportItem ReprocessItem(string path, string downloadId, int movieId, string releaseGroup, QualityModel quality, List <Language> languages) { var rootFolder = Path.GetDirectoryName(path); var movie = _movieService.GetMovie(movieId); var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem; var languageParse = LanguageParser.ParseLanguages(path); if (languageParse.Count <= 1 && languageParse.First() == Language.Unknown && movie != null) { languageParse = new List <Language> { movie.OriginalLanguage }; _logger.Debug("Language couldn't be parsed from release, fallback to movie original language: {0}", movie.OriginalLanguage.Name); } var localEpisode = new LocalMovie { Movie = movie, FileMovieInfo = Parser.Parser.ParseMoviePath(path), DownloadClientMovieInfo = downloadClientItem == null ? null : Parser.Parser.ParseMovieTitle(downloadClientItem.Title), Path = path, SceneSource = SceneSource(movie, rootFolder), ExistingFile = movie.Path.IsParentPath(path), Size = _diskProvider.GetFileSize(path), Languages = (languages?.SingleOrDefault() ?? Language.Unknown) == Language.Unknown ? languageParse : languages, Quality = quality.Quality == Quality.Unknown ? QualityParser.ParseQuality(path) : quality, ReleaseGroup = releaseGroup.IsNullOrWhiteSpace() ? Parser.Parser.ParseReleaseGroup(path) : releaseGroup, }; return(MapItem(_importDecisionMaker.GetDecision(localEpisode, downloadClientItem), rootFolder, downloadId, null)); }
public static QualityModel ParseQuality(string name) { Logger.Debug("Trying to parse quality for {0}", name); name = name.Trim(); var normalizedName = name.Replace('_', ' ').Trim().ToLower(); var result = new QualityModel { Quality = Quality.Unknown }; result.Proper = ProperRegex.IsMatch(normalizedName); if (RawHDRegex.IsMatch(normalizedName)) { result.Quality = Quality.RAWHD; return(result); } var sourceMatch = SourceRegex.Match(normalizedName); var resolution = ParseResolution(normalizedName); var codecRegex = CodecRegex.Match(normalizedName); if (sourceMatch.Groups["bluray"].Success) { if (codecRegex.Groups["xvid"].Success || codecRegex.Groups["divx"].Success) { result.Quality = Quality.DVD; return(result); } if (resolution == Resolution._1080p) { result.Quality = Quality.Bluray1080p; return(result); } if (resolution == Resolution._480p || resolution == Resolution._576p) { result.Quality = Quality.DVD; return(result); } result.Quality = Quality.Bluray720p; return(result); } if (sourceMatch.Groups["webdl"].Success) { if (resolution == Resolution._1080p) { result.Quality = Quality.WEBDL1080p; return(result); } if (resolution == Resolution._720p) { result.Quality = Quality.WEBDL720p; return(result); } if (name.Contains("[WEBDL]")) { result.Quality = Quality.WEBDL720p; return(result); } result.Quality = Quality.WEBDL480p; return(result); } if (sourceMatch.Groups["hdtv"].Success) { if (resolution == Resolution._1080p) { result.Quality = Quality.HDTV1080p; return(result); } if (resolution == Resolution._720p) { result.Quality = Quality.HDTV720p; return(result); } if (name.Contains("[HDTV]")) { result.Quality = Quality.HDTV720p; return(result); } result.Quality = Quality.SDTV; return(result); } if (sourceMatch.Groups["bdrip"].Success || sourceMatch.Groups["brrip"].Success) { if (resolution == Resolution._720p) { result.Quality = Quality.Bluray720p; return(result); } else if (resolution == Resolution._1080p) { result.Quality = Quality.Bluray1080p; return(result); } else { result.Quality = Quality.DVD; return(result); } } if (sourceMatch.Groups["dvd"].Success) { result.Quality = Quality.DVD; return(result); } if (sourceMatch.Groups["pdtv"].Success || sourceMatch.Groups["sdtv"].Success || sourceMatch.Groups["dsr"].Success) { result.Quality = Quality.SDTV; return(result); } if (resolution == Resolution._1080p) { result.Quality = Quality.HDTV1080p; return(result); } if (resolution == Resolution._720p) { result.Quality = Quality.HDTV720p; return(result); } if (codecRegex.Groups["x264"].Success) { result.Quality = Quality.SDTV; return(result); } if (normalizedName.Contains("bluray720p")) { result.Quality = Quality.Bluray720p; } if (normalizedName.Contains("bluray1080p")) { result.Quality = Quality.Bluray1080p; } //Based on extension if (result.Quality == Quality.Unknown && !name.ContainsInvalidPathChars()) { try { result.Quality = MediaFileExtensions.GetQualityForExtension(Path.GetExtension(name)); } catch (ArgumentException) { //Swallow exception for cases where string contains illegal //path characters. } } return(result); }
public bool CutoffNotMet(Profile profile, QualityModel currentQuality, List <CustomFormat> currentFormats, QualityModel newQuality = null) { return(QualityCutoffNotMet(profile, currentQuality, newQuality) || CustomFormatCutoffNotMet(profile, currentFormats)); }
public void UpdateQuality(QualityModel quality) { new UpdateQualityOperation(_repository).Execute(quality); }
private List <ImportDecision> ProcessFiles(Series series, QualityModel quality, params string[] videoFiles) { var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), series, true, quality); return(_importApprovedEpisodes.Import(decisions, true)); }
public bool IsUpgradable(QualityProfile qualityProfile, List <QualityModel> currentQualities, int currentScore, QualityModel newQuality, int newScore) { var qualityUpgrade = IsQualityUpgradable(qualityProfile, currentQualities, newQuality); if (qualityUpgrade == ProfileComparisonResult.Upgrade) { _logger.Debug("New item has a better quality"); return(true); } if (qualityUpgrade == ProfileComparisonResult.Downgrade) { _logger.Debug("Existing item has better quality, skipping"); return(false); } if (!IsPreferredWordUpgradable(currentScore, newScore)) { _logger.Debug("Existing item has a better preferred word score, skipping"); return(false); } _logger.Debug("New item has a better preferred word score"); return(true); }
public bool CutoffNotMet(QualityProfile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, int currentScore, QualityModel newQuality = null, int newScore = 0) { // If we can upgrade the language (it is not the cutoff) then the quality doesn't // matter as we can always get same quality with prefered language. if (LanguageCutoffNotMet(languageProfile, currentLanguage)) { return(true); } if (QualityCutoffNotMet(profile, currentQuality, newQuality)) { return(true); } if (IsPreferredWordUpgradable(currentScore, newScore)) { return(true); } _logger.Debug("Existing item meets cut-off. skipping."); return(false); }
public ManualImportItem ReprocessItem(string path, string downloadId, int seriesId, int?seasonNumber, List <int> episodeIds, string releaseGroup, QualityModel quality, Language language) { var rootFolder = Path.GetDirectoryName(path); var series = _seriesService.GetSeries(seriesId); if (episodeIds.Any()) { var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem; var localEpisode = new LocalEpisode(); localEpisode.Series = series; localEpisode.Episodes = _episodeService.GetEpisodes(episodeIds); localEpisode.FileEpisodeInfo = Parser.Parser.ParsePath(path); localEpisode.DownloadClientEpisodeInfo = downloadClientItem == null ? null : Parser.Parser.ParseTitle(downloadClientItem.Title); localEpisode.Path = path; localEpisode.SceneSource = SceneSource(series, rootFolder); localEpisode.ExistingFile = series.Path.IsParentPath(path); localEpisode.Size = _diskProvider.GetFileSize(path); localEpisode.ReleaseGroup = releaseGroup.IsNullOrWhiteSpace() ? Parser.Parser.ParseReleaseGroup(path) : releaseGroup; localEpisode.Language = language == Language.Unknown ? LanguageParser.ParseLanguage(path) : language; localEpisode.Quality = quality.Quality == Quality.Unknown ? QualityParser.ParseQuality(path) : quality; return(MapItem(_importDecisionMaker.GetDecision(localEpisode, downloadClientItem), rootFolder, downloadId, null)); } // This case will happen if the user selected a season, but didn't select the episodes in the season then changed the language or quality. // Instead of overriding their season selection let it persist and reject it with an appropriate error. if (seasonNumber.HasValue) { var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem; var localEpisode = new LocalEpisode { Series = series, Episodes = new List <Episode>(), FileEpisodeInfo = Parser.Parser.ParsePath(path), DownloadClientEpisodeInfo = downloadClientItem == null ? null : Parser.Parser.ParseTitle(downloadClientItem.Title), Path = path, SceneSource = SceneSource(series, rootFolder), ExistingFile = series.Path.IsParentPath(path), Size = _diskProvider.GetFileSize(path), ReleaseGroup = releaseGroup.IsNullOrWhiteSpace() ? Parser.Parser.ParseReleaseGroup(path) : releaseGroup, Language = language == Language.Unknown ? LanguageParser.ParseLanguage(path) : language, Quality = quality.Quality == Quality.Unknown ? QualityParser.ParseQuality(path) : quality }; return(MapItem(new ImportDecision(localEpisode, new Rejection("Episodes not selected")), rootFolder, downloadId, null)); } return(ProcessFile(rootFolder, rootFolder, path, downloadId, series)); }
public bool IsUpgradable(QualityProfile qualityProfile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, int currentScore, QualityModel newQuality, Language newLanguage, int newScore) { var qualityComparer = new QualityModelComparer(qualityProfile); var qualityCompare = qualityComparer.Compare(newQuality?.Quality, currentQuality.Quality); 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); } // 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 (_configService.DownloadPropersAndRepacks != ProperDownloadTypes.DoNotPrefer && newQuality?.Revision.CompareTo(currentQuality.Revision) > 0) { _logger.Debug("New item has a better quality revision"); return(true); } var languageCompare = new LanguageComparer(languageProfile).Compare(newLanguage, currentLanguage); if (languageCompare > 0) { _logger.Debug("New item has a more preferred language"); return(true); } if (languageCompare < 0) { _logger.Debug("Existing item has better language, skipping"); return(false); } if (!IsPreferredWordUpgradable(currentScore, newScore)) { _logger.Debug("Existing item has a better preferred word score, skipping"); return(false); } _logger.Debug("New item has a better preferred word score"); return(true); }
public List <ImportDecision> GetImportDecisions(List <string> videoFiles, Series series, bool sceneSource, QualityModel quality = null) { var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series); _logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count()); return(GetDecisions(newFiles, series, sceneSource, quality).ToList()); }
public void Setup() { _albumpass1 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >(); _albumpass2 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >(); _albumpass3 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >(); _albumfail1 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >(); _albumfail2 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >(); _albumfail3 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >(); _pass1 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >(); _pass2 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >(); _pass3 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >(); _fail1 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >(); _fail2 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >(); _fail3 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >(); _albumpass1.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Accept()); _albumpass2.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Accept()); _albumpass3.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Accept()); _albumfail1.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Reject("_albumfail1")); _albumfail2.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Reject("_albumfail2")); _albumfail3.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Reject("_albumfail3")); _pass1.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Accept()); _pass2.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Accept()); _pass3.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Accept()); _fail1.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Reject("_fail1")); _fail2.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Reject("_fail2")); _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Reject("_fail3")); _artist = Builder <Artist> .CreateNew() .With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() }) .Build(); _albumRelease = Builder <AlbumRelease> .CreateNew() .Build(); _quality = new QualityModel(Quality.MP3_256); _localTrack = new LocalTrack { Artist = _artist, Quality = _quality, Tracks = new List <Track> { new Track() }, Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi".AsOsAgnostic() }; GivenAudioFiles(new List <string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi".AsOsAgnostic() }); Mocker.GetMock <IIdentificationService>() .Setup(s => s.Identify(It.IsAny <List <LocalTrack> >(), It.IsAny <Artist>(), It.IsAny <Album>(), It.IsAny <AlbumRelease>(), It.IsAny <bool>(), It.IsAny <bool>(), It.IsAny <bool>())) .Returns((List <LocalTrack> tracks, Artist artist, Album album, AlbumRelease release, bool newDownload, bool singleRelease, bool includeExisting) => { var ret = new LocalAlbumRelease(tracks); ret.AlbumRelease = _albumRelease; return(new List <LocalAlbumRelease> { ret }); }); Mocker.GetMock <IMediaFileService>() .Setup(c => c.FilterUnchangedFiles(It.IsAny <List <IFileInfo> >(), It.IsAny <Artist>(), It.IsAny <FilterFilesType>())) .Returns((List <IFileInfo> files, Artist artist, FilterFilesType filter) => files); GivenSpecifications(_albumpass1); }
public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { var source = QualitySource.Unknown; var sourceConfidence = Confidence.Default; var resolution = 0; var resolutionConfidence = Confidence.Default; var revision = new Revision(1); var revisionConfidence = Confidence.Default; foreach (var augmentQuality in _augmentQualities) { var augmentedQuality = augmentQuality.AugmentQuality(localEpisode, downloadClientItem); if (augmentedQuality == null) { continue; } _logger.Trace("Considering Source {0} ({1}) Resolution {2} ({3}) Revision {4} from {5}", augmentedQuality.Source, augmentedQuality.SourceConfidence, augmentedQuality.Resolution, augmentedQuality.ResolutionConfidence, augmentedQuality.Revision, augmentQuality.Name); if (source == QualitySource.Unknown || augmentedQuality.SourceConfidence > sourceConfidence && augmentedQuality.Source != QualitySource.Unknown) { source = augmentedQuality.Source; sourceConfidence = augmentedQuality.SourceConfidence; } if (resolution == 0 || augmentedQuality.ResolutionConfidence > resolutionConfidence && augmentedQuality.Resolution > 0) { resolution = augmentedQuality.Resolution; resolutionConfidence = augmentedQuality.ResolutionConfidence; } if (augmentedQuality.Revision != null) { // Update the revision and confidence if it is higher than the current confidence, // this will allow explicitly detected v0 to override the v1 default. if (augmentedQuality.RevisionConfidence > revisionConfidence) { revision = augmentedQuality.Revision; revisionConfidence = augmentedQuality.RevisionConfidence; } // Update the revision and confidence if it is the same confidence and the revision is higher, // this will allow the best revision to be used in the event there is a disagreement. else if (augmentedQuality.RevisionConfidence == revisionConfidence && augmentedQuality.Revision > revision) { revision = augmentedQuality.Revision; revisionConfidence = augmentedQuality.RevisionConfidence; } } } _logger.Trace("Selected Source {0} ({1}) Resolution {2} ({3}) Revision {4}", source, sourceConfidence, resolution, resolutionConfidence, revision); var quality = new QualityModel(QualityFinder.FindBySourceAndResolution(source, resolution), revision); if (resolutionConfidence == Confidence.MediaInfo) { quality.ResolutionDetectionSource = QualityDetectionSource.MediaInfo; } else if (resolutionConfidence == Confidence.Fallback) { quality.ResolutionDetectionSource = QualityDetectionSource.Extension; } else { quality.ResolutionDetectionSource = QualityDetectionSource.Name; } if (sourceConfidence == Confidence.Fallback) { quality.SourceDetectionSource = QualityDetectionSource.Extension; } else { quality.SourceDetectionSource = QualityDetectionSource.Name; } quality.RevisionDetectionSource = revisionConfidence == Confidence.Tag ? QualityDetectionSource.Name : QualityDetectionSource.Unknown; _logger.Debug("Using quality: {0}", quality); localEpisode.Quality = quality; return(localEpisode); }
public bool CutoffNotMet(QualityProfile profile, List <QualityModel> currentQualities, int currentScore, QualityModel newQuality = null, int newScore = 0) { foreach (var quality in currentQualities) { if (QualityCutoffNotMet(profile, quality, newQuality)) { return(true); } } if (IsPreferredWordUpgradable(currentScore, newScore)) { return(true); } _logger.Debug("Existing item meets cut-off. skipping."); return(false); }
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { if (searchCriteria != null && searchCriteria.UserInvokedSearch) { _logger.Debug("Ignoring delay for user invoked search"); return(Decision.Accept()); } var profile = subject.Movie.Profile.Value; var delayProfile = _delayProfileService.BestForTags(subject.Movie.Tags); var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol); var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol; // Preferred word count var title = subject.Release.Title; var preferredWords = subject.Movie.Profile.Value.PreferredTags; var preferredCount = 0; if (preferredWords == null) { preferredCount = 1; _logger.Debug("Preferred words is null, setting preffered count to 1."); } else { preferredCount = preferredWords.AsEnumerable().Count(w => title.ToLower().Contains(w.ToLower())); } if (delay == 0) { _logger.Debug("Profile does not require a waiting period before download for {0}.", subject.Release.DownloadProtocol); return(Decision.Accept()); } var comparer = new QualityModelComparer(profile); if (isPreferredProtocol && (subject.Movie.MovieFileId != 0 && subject.Movie.MovieFile != null) && (preferredCount > 0 || preferredWords == null)) { var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, subject.Movie.MovieFile.Value.Quality, subject.ParsedMovieInfo.Quality); if (upgradable) { var revisionUpgrade = _qualityUpgradableSpecification.IsRevisionUpgrade(subject.Movie.MovieFile.Value.Quality, subject.ParsedMovieInfo.Quality); if (revisionUpgrade) { _logger.Debug("New quality is a better revision for existing quality and preferred word count is {0}, skipping delay", preferredCount); return(Decision.Accept()); } } } // If quality meets or exceeds the best allowed quality in the profile accept it immediately var bestQualityInProfile = new QualityModel(profile.LastAllowedQuality()); var isBestInProfile = comparer.Compare(subject.ParsedMovieInfo.Quality, bestQualityInProfile) >= 0; if (isBestInProfile && isPreferredProtocol && (preferredCount > 0 || preferredWords == null)) { _logger.Debug("Quality is highest in profile for preferred protocol and preferred word count is {0}, will not delay.", preferredCount); return(Decision.Accept()); } var oldest = _pendingReleaseService.OldestPendingRelease(subject.Movie.Id); if (oldest != null && oldest.Release.AgeMinutes > delay) { return(Decision.Accept()); } if (subject.Release.AgeMinutes < delay) { _logger.Debug("Waiting for better quality release, There is a {0} minute delay on {1}", delay, subject.Release.DownloadProtocol); return(Decision.Reject("Waiting for better quality release")); } return(Decision.Accept()); }
private ProfileComparisonResult IsQualityUpgradable(QualityProfile profile, List <QualityModel> currentQualities, QualityModel newQuality = null) { if (newQuality != null) { var totalCompare = 0; foreach (var quality in currentQualities) { var compare = new QualityModelComparer(profile).Compare(newQuality, quality); totalCompare += compare; if (compare < 0) { // Not upgradable if new quality is a downgrade for any current quality return(ProfileComparisonResult.Downgrade); } } // Not upgradable if new quality is equal to all current qualities if (totalCompare == 0) { return(ProfileComparisonResult.Equal); } // Quality Treated as Equal if Propers are not Prefered if (_configService.DownloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer && newQuality.Revision.CompareTo(currentQualities.Min(q => q.Revision)) > 0) { return(ProfileComparisonResult.Equal); } } return(ProfileComparisonResult.Upgrade); }
private ImportDecision GetDecision(string file, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldUseFolderName, bool shouldCheckQuality = false) { ImportDecision decision = null; try { var localMovie = _parsingService.GetLocalMovie(file, movie, shouldUseFolderName ? folderInfo : null, sceneSource); if (localMovie != null) { localMovie.Quality = GetQuality(folderInfo, localMovie.Quality, movie); localMovie.Size = _diskProvider.GetFileSize(file); _logger.Debug("Size: {0}", localMovie.Size); var current = localMovie.Quality; //TODO: make it so media info doesn't ruin the import process of a new series if (sceneSource && ShouldCheckQualityForParsedQuality(current.Quality)) { localMovie.MediaInfo = _videoFileInfoReader.GetMediaInfo(file); if (shouldCheckQuality) { _logger.Debug("Checking quality for this video file to make sure nothing mismatched."); var width = localMovie.MediaInfo.Width; var qualityName = current.Quality.Name.ToLower(); QualityModel updated = null; if (width > 2000) { if (qualityName.Contains("bluray")) { updated = new QualityModel(Quality.Bluray2160p); } else if (qualityName.Contains("webdl")) { updated = new QualityModel(Quality.WEBDL2160p); } else if (qualityName.Contains("hdtv")) { updated = new QualityModel(Quality.HDTV2160p); } else { var def = _qualitiesService.Get(Quality.HDTV2160p); if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size) { updated = new QualityModel(Quality.HDTV2160p); } def = _qualitiesService.Get(Quality.WEBDL2160p); if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size) { updated = new QualityModel(Quality.WEBDL2160p); } def = _qualitiesService.Get(Quality.Bluray2160p); if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size) { updated = new QualityModel(Quality.Bluray2160p); } if (updated == null) { updated = new QualityModel(Quality.Bluray2160p); } } } else if (width > 1400) { if (qualityName.Contains("bluray")) { updated = new QualityModel(Quality.Bluray1080p); } else if (qualityName.Contains("webdl")) { updated = new QualityModel(Quality.WEBDL1080p); } else if (qualityName.Contains("hdtv")) { updated = new QualityModel(Quality.HDTV1080p); } else { var def = _qualitiesService.Get(Quality.HDTV1080p); if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size) { updated = new QualityModel(Quality.HDTV1080p); } def = _qualitiesService.Get(Quality.WEBDL1080p); if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size) { updated = new QualityModel(Quality.WEBDL1080p); } def = _qualitiesService.Get(Quality.Bluray1080p); if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size) { updated = new QualityModel(Quality.Bluray1080p); } if (updated == null) { updated = new QualityModel(Quality.Bluray1080p); } } } else if (width > 900) { if (qualityName.Contains("bluray")) { updated = new QualityModel(Quality.Bluray720p); } else if (qualityName.Contains("webdl")) { updated = new QualityModel(Quality.WEBDL720p); } else if (qualityName.Contains("hdtv")) { updated = new QualityModel(Quality.HDTV720p); } else { var def = _qualitiesService.Get(Quality.HDTV720p); if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size) { updated = new QualityModel(Quality.HDTV720p); } def = _qualitiesService.Get(Quality.WEBDL720p); if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size) { updated = new QualityModel(Quality.WEBDL720p); } def = _qualitiesService.Get(Quality.Bluray720p); if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size) { updated = new QualityModel(Quality.Bluray720p); } if (updated == null) { updated = new QualityModel(Quality.Bluray720p); } } } if (updated != null && updated != current) { _logger.Debug("Quality ({0}) of the file is different than the one we have ({1})", updated, current); updated.QualitySource = QualitySource.MediaInfo; localMovie.Quality = updated; } } decision = GetDecision(localMovie); } else { decision = GetDecision(localMovie); } } else { localMovie = new LocalMovie(); localMovie.Path = file; decision = new ImportDecision(localMovie, new Rejection("Unable to parse file")); } } catch (Exception e) { _logger.Error(e, "Couldn't import file. " + file); var localMovie = new LocalMovie { Path = file }; decision = new ImportDecision(localMovie, new Rejection("Unexpected error processing file")); } //LocalMovie nullMovie = null; //decision = new ImportDecision(nullMovie, new Rejection("IMPLEMENTATION MISSING!!!")); return(decision); }
public bool QualityCutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null) { var cutoffCompare = new QualityModelComparer(profile).Compare(currentQuality.Quality.Id, profile.Cutoff); if (cutoffCompare < 0) { return(true); } if (newQuality != null && IsRevisionUpgrade(currentQuality, newQuality)) { return(true); } return(false); }
public bool CutoffNotMet(Profile profile, QualityModel currentQuality, List <CustomFormat> 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 IsUpgradeAllowed(Profile qualityProfile, QualityModel currentQuality, List <CustomFormat> currentCustomFormats, QualityModel newQuality, List <CustomFormat> 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); }
public bool IsUpgradable(Profile profile, QualityModel currentQuality, List <CustomFormat> currentCustomFormats, QualityModel newQuality, List <CustomFormat> 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); }
private IEnumerable <ImportDecision> GetDecisions(IEnumerable <String> videoFiles, Series series, bool sceneSource, QualityModel quality = null) { foreach (var file in videoFiles) { ImportDecision decision = null; try { var localEpisode = _parsingService.GetLocalEpisode(file, series, sceneSource); if (localEpisode != null) { if (quality != null && new QualityModelComparer(localEpisode.Series.Profile).Compare(quality, localEpisode.Quality) > 0) { _logger.Debug("Using quality from folder: {0}", quality); localEpisode.Quality = quality; } localEpisode.Size = _diskProvider.GetFileSize(file); _logger.Debug("Size: {0}", localEpisode.Size); //TODO: make it so media info doesn't ruin the import process of a new series if (sceneSource) { localEpisode.MediaInfo = _videoFileInfoReader.GetMediaInfo(file); } decision = GetDecision(localEpisode); } else { localEpisode = new LocalEpisode(); localEpisode.Path = file; decision = new ImportDecision(localEpisode, "Unable to parse file"); } } catch (EpisodeNotFoundException e) { var localEpisode = new LocalEpisode(); localEpisode.Path = file; decision = new ImportDecision(localEpisode, e.Message); } catch (Exception e) { _logger.ErrorException("Couldn't import file. " + file, e); } if (decision != null) { yield return(decision); } } }
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { //How do we want to handle drone being off and the automatic search being triggered? //TODO: Add a flag to the search to state it is a "scheduled" search if (searchCriteria != null) { _logger.Debug("Ignore delay for searches"); return(true); } var profile = subject.Series.Profile.Value; if (profile.GrabDelay == 0) { _logger.Debug("Profile does not delay before download"); return(true); } var comparer = new QualityModelComparer(profile); foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) { var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, file.Quality, subject.ParsedEpisodeInfo.Quality); if (upgradable) { var revisionUpgrade = _qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality); if (revisionUpgrade) { _logger.Debug("New quality is a better revision for existing quality, skipping delay"); return(true); } } } //If quality meets or exceeds the best allowed quality in the profile accept it immediately var bestQualityInProfile = new QualityModel(profile.Items.Last(q => q.Allowed).Quality); var bestCompare = comparer.Compare(subject.ParsedEpisodeInfo.Quality, bestQualityInProfile); if (bestCompare >= 0) { _logger.Debug("Quality is highest in profile, will not delay"); return(true); } if (profile.GrabDelayMode == GrabDelayMode.Cutoff) { var cutoff = new QualityModel(profile.Cutoff); var cutoffCompare = comparer.Compare(subject.ParsedEpisodeInfo.Quality, cutoff); if (cutoffCompare >= 0) { _logger.Debug("Quality meets or exceeds the cutoff, will not delay"); return(true); } } if (profile.GrabDelayMode == GrabDelayMode.First) { var episodeIds = subject.Episodes.Select(e => e.Id); var oldest = _pendingReleaseService.GetPendingRemoteEpisodes(subject.Series.Id) .Where(r => r.Episodes.Select(e => e.Id).Intersect(episodeIds).Any()) .OrderByDescending(p => p.Release.AgeHours) .FirstOrDefault(); if (oldest != null && oldest.Release.AgeHours > profile.GrabDelay) { return(true); } } if (subject.Release.AgeHours < profile.GrabDelay) { _logger.Debug("Age ({0}) is less than delay {1}, delaying", subject.Release.AgeHours, profile.GrabDelay); return(false); } return(true); }