From 77a5fd62d27f7334c7b672e1c81286d0939a12fa Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 3 Nov 2013 22:39:37 -0800 Subject: [PATCH] Better sample checks Fixed: Sample checking relies on runtime instead of file size (Windows) Fixed: Minimum file size for 1080p releases is now 140MB (lower will be considered samples) --- .../NotSampleSpecificationFixture.cs | 142 +++++++++--------- .../DownloadedEpisodesImportService.cs | 6 +- .../EpisodeImport/ImportApprovedEpisodes.cs | 2 +- .../Specifications/NotSampleSpecification.cs | 49 ++++-- 4 files changed, 114 insertions(+), 85 deletions(-) diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs index 3699ffeb6..e6c6d0ad1 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; using NzbDrone.Test.Common; @@ -36,106 +37,46 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications { Path = @"C:\Test\30 Rock\30.rock.s01e01.avi", Episodes = episodes, - Series = _series + Series = _series, + Quality = new QualityModel(Quality.HDTV720p) }; } - private void WithDailySeries() - { - _series.SeriesType = SeriesTypes.Daily; - } - - private void WithSeasonZero() - { - _localEpisode.Episodes[0].SeasonNumber = 0; - } - - private void WithFileSize(long size) + private void GivenFileSize(long size) { _localEpisode.Size = size; } - private void WithLength(int minutes) + private void GivenRuntime(int seconds) { Mocker.GetMock() .Setup(s => s.GetRunTime(It.IsAny())) - .Returns(new TimeSpan(0, 0, minutes, 0)); + .Returns(new TimeSpan(0, 0, seconds)); } [Test] public void should_return_true_if_series_is_daily() { - WithDailySeries(); - + _series.SeriesType = SeriesTypes.Daily; Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); } [Test] public void should_return_true_if_season_zero() { - WithSeasonZero(); - + _localEpisode.Episodes[0].SeasonNumber = 0; Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); } [Test] - public void should_return_false_if_undersize_and_under_length() + public void should_return_true_for_existing_file() { - WithFileSize(10.Megabytes()); - WithLength(1); - - Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); - } - - [Test] - public void should_return_true_if_undersize() - { - WithFileSize(10.Megabytes()); - WithLength(10); - + _localEpisode.ExistingFile = true; Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); } [Test] - public void should_return_true_if_under_length() - { - WithFileSize(100.Megabytes()); - WithLength(1); - - Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); - } - - [Test] - public void should_return_true_if_over_size_and_length() - { - WithFileSize(100.Megabytes()); - WithLength(10); - - Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); - } - - [Test] - public void should_not_check_lenght_if_file_is_large_enough() - { - WithFileSize(100.Megabytes()); - - Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); - - Mocker.GetMock().Verify(c => c.GetRunTime(It.IsAny()), Times.Never()); - } - - [Test] - public void should_log_error_if_run_time_is_0_and_under_sample_size() - { - WithFileSize(40.Megabytes()); - WithLength(0); - - Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); - ExceptionVerification.ExpectedErrors(1); - } - - [Test] - public void should_skip_check_for_flv_file() + public void should_return_true_for_flv() { _localEpisode.Path = @"C:\Test\some.show.s01e01.flv"; @@ -143,5 +84,66 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications Mocker.GetMock().Verify(c => c.GetRunTime(It.IsAny()), Times.Never()); } + + [Test] + public void should_not_run_runtime_check_on_linux() + { + LinuxOnly(); + GivenFileSize(1000.Megabytes()); + + Subject.IsSatisfiedBy(_localEpisode); + + Mocker.GetMock().Verify(v => v.GetRunTime(It.IsAny()), Times.Never()); + } + + [Test] + public void should_run_runtime_check_on_windows() + { + GivenRuntime(120); + GivenFileSize(1000.Megabytes()); + + Subject.IsSatisfiedBy(_localEpisode); + + Mocker.GetMock().Verify(v => v.GetRunTime(It.IsAny()), Times.Once()); + } + + [Test] + public void should_return_false_if_runtime_is_less_than_minimum() + { + GivenRuntime(60); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); + } + + [Test] + public void should_return_true_if_runtime_greater_than_than_minimum() + { + GivenRuntime(120); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); + } + + [Test] + public void should_return_false_if_file_size_is_under_minimum() + { + LinuxOnly(); + + GivenRuntime(120); + GivenFileSize(20.Megabytes()); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); + } + + [Test] + public void should_return_false_if_file_size_is_under_minimum_for_larger_limits() + { + LinuxOnly(); + + GivenRuntime(120); + GivenFileSize(120.Megabytes()); + _localEpisode.Quality = new QualityModel(Quality.Bluray1080p); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); + } } } diff --git a/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs b/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs index 5f36b5910..4cb226005 100644 --- a/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs @@ -7,7 +7,6 @@ using NzbDrone.Common; using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.MediaFiles.EpisodeImport; -using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Parser; using NzbDrone.Core.Tv; @@ -74,10 +73,7 @@ namespace NzbDrone.Core.MediaFiles if (importedFiles.Any()) { - if (_diskProvider.GetFolderSize(subFolder) < NotSampleSpecification.SampleSizeLimit) - { - _diskProvider.DeleteFolder(subFolder, true); - } + _diskProvider.DeleteFolder(subFolder, true); } } catch (Exception e) diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs index 86d10675e..ce14d7054 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs @@ -41,7 +41,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport var qualifiedImports = GetQualifiedImports(decisions); var imported = new List(); - foreach (var importDecision in qualifiedImports) + foreach (var importDecision in qualifiedImports.OrderByDescending(e => e.LocalEpisode.Size)) { var localEpisode = importDecision.LocalEpisode; diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs index dba6cf400..ccf833ce7 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs @@ -1,8 +1,11 @@ using System; +using System.Collections.Generic; using System.IO; using NLog; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; using NzbDrone.Core.Tv; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications @@ -11,6 +14,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications { private readonly IVideoFileInfoReader _videoFileInfoReader; private readonly Logger _logger; + private static List _largeSampleSizeQualities = new List { Quality.HDTV1080p, Quality.WEBDL1080p, Quality.Bluray1080p }; public NotSampleSpecification(IVideoFileInfoReader videoFileInfoReader, Logger logger) @@ -31,6 +35,12 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications public bool IsSatisfiedBy(LocalEpisode localEpisode) { + if (localEpisode.ExistingFile) + { + _logger.Trace("Existing file, skipping sample check"); + return true; + } + if (localEpisode.Series.SeriesType == SeriesTypes.Daily) { _logger.Trace("Daily Series, skipping sample check"); @@ -43,28 +53,49 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications return true; } - if (Path.GetExtension(localEpisode.Path).Equals(".flv", StringComparison.InvariantCultureIgnoreCase)) + var extension = Path.GetExtension(localEpisode.Path); + + if (extension != null && extension.Equals(".flv", StringComparison.InvariantCultureIgnoreCase)) { - _logger.Trace("Skipping smaple check for .flv file"); + _logger.Trace("Skipping sample check for .flv file"); return true; } - if (localEpisode.Size > SampleSizeLimit) + if (OsInfo.IsWindows) { + var runTime = _videoFileInfoReader.GetRunTime(localEpisode.Path); + + if (runTime.TotalMinutes.Equals(0)) + { + _logger.Error("[{0}] has a runtime of 0, is it a valid video file?", localEpisode); + return false; + } + + if (runTime.TotalSeconds < 90) + { + _logger.Trace("[{0}] appears to be a sample. Size: {1} Runtime: {2}", localEpisode.Path, localEpisode.Size, runTime); + return false; + } + + _logger.Trace("Runtime is over 2 minutes, skipping file size check"); return true; } - var runTime = _videoFileInfoReader.GetRunTime(localEpisode.Path); + return CheckSize(localEpisode); + } - if (runTime.TotalMinutes.Equals(0)) + private bool CheckSize(LocalEpisode localEpisode) + { + if (_largeSampleSizeQualities.Contains(localEpisode.Quality.Quality)) { - _logger.Error("[{0}] has a runtime of 0, is it a valid video file?", localEpisode); - return false; + if (localEpisode.Size < SampleSizeLimit * 2) + { + return false; + } } - if (runTime.TotalMinutes < 3) + if (localEpisode.Size < SampleSizeLimit) { - _logger.Trace("[{0}] appears to be a sample. Size: {1} Runtime: {2}", localEpisode.Path, localEpisode.Size, runTime); return false; }