diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/DetectSampleFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/DetectSampleFixture.cs index e7dd0f903..cb5e45de5 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/DetectSampleFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/DetectSampleFixture.cs @@ -39,22 +39,29 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport Path = @"C:\Test\30 Rock\30.rock.s01e01.avi", Episodes = episodes, Series = _series, - Quality = new QualityModel(Quality.HDTV720p) + Quality = new QualityModel(Quality.HDTV720p), }; } private void GivenRuntime(int seconds) { + var runtime = new TimeSpan(0, 0, seconds); + Mocker.GetMock() .Setup(s => s.GetRunTime(It.IsAny())) - .Returns(new TimeSpan(0, 0, seconds)); + .Returns(runtime); + + _localEpisode.MediaInfo = Builder.CreateNew().With(m => m.RunTime = runtime).Build(); } [Test] public void should_return_false_if_season_zero() { _localEpisode.Episodes[0].SeasonNumber = 0; - ShouldBeNotSample(); + + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample); } [Test] @@ -62,7 +69,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport { _localEpisode.Path = @"C:\Test\some.show.s01e01.flv"; - ShouldBeNotSample(); + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample); Mocker.GetMock().Verify(c => c.GetRunTime(It.IsAny()), Times.Never()); } @@ -72,7 +81,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport { _localEpisode.Path = @"C:\Test\some.show.s01e01.strm"; - ShouldBeNotSample(); + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample); Mocker.GetMock().Verify(c => c.GetRunTime(It.IsAny()), Times.Never()); } @@ -94,7 +105,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport { GivenRuntime(60); - ShouldBeSample(); + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.Sample); } [Test] @@ -102,7 +115,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport { GivenRuntime(600); - ShouldBeNotSample(); + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample); } [Test] @@ -111,7 +126,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport _series.Runtime = 6; GivenRuntime(299); - ShouldBeNotSample(); + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample); } [Test] @@ -120,7 +137,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport _series.Runtime = 2; GivenRuntime(60); - ShouldBeNotSample(); + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample); } [Test] @@ -129,7 +148,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport _series.Runtime = 2; GivenRuntime(10); - ShouldBeSample(); + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.Sample); } [Test] @@ -152,7 +173,10 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport GivenRuntime(600); _series.SeriesType = SeriesTypes.Daily; _localEpisode.Episodes[0].SeasonNumber = 0; - ShouldBeNotSample(); + + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample); } [Test] @@ -161,21 +185,33 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport _series.SeriesType = SeriesTypes.Anime; _localEpisode.Episodes[0].SeasonNumber = 0; - ShouldBeNotSample(); + Subject.IsSample(_localEpisode.Series, + _localEpisode.Path, + _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample); } - private void ShouldBeSample() + [Test] + public void should_use_runtime_from_media_info() { - Subject.IsSample(_localEpisode.Series, - _localEpisode.Path, - _localEpisode.IsSpecial).Should().Be(DetectSampleResult.Sample); + GivenRuntime(120); + + _localEpisode.Series.Runtime = 30; + _localEpisode.Episodes.First().Runtime = 30; + + Subject.IsSample(_localEpisode).Should().Be(DetectSampleResult.Sample); + + Mocker.GetMock().Verify(v => v.GetRunTime(It.IsAny()), Times.Never()); } - private void ShouldBeNotSample() + [Test] + public void should_use_runtime_from_episode_over_series() { - Subject.IsSample(_localEpisode.Series, - _localEpisode.Path, - _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample); + GivenRuntime(120); + + _localEpisode.Series.Runtime = 5; + _localEpisode.Episodes.First().Runtime = 30; + + Subject.IsSample(_localEpisode).Should().Be(DetectSampleResult.Sample); } } } diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs index 149239632..fb47e37db 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/DetectSample.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/DetectSample.cs index b7c225eb2..e718840d1 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/DetectSample.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/DetectSample.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.IO; using NLog; using NzbDrone.Core.MediaFiles.MediaInfo; +using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; namespace NzbDrone.Core.MediaFiles.EpisodeImport @@ -9,6 +10,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport public interface IDetectSample { DetectSampleResult IsSample(Series series, string path, bool isSpecial); + DetectSampleResult IsSample(LocalEpisode localEpisode); } public class DetectSample : IDetectSample @@ -23,6 +25,51 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport } public DetectSampleResult IsSample(Series series, string path, bool isSpecial) + { + var extensionResult = IsSample(path, isSpecial); + + if (extensionResult != DetectSampleResult.Indeterminate) + { + return extensionResult; + } + + var fileRuntime = _videoFileInfoReader.GetRunTime(path); + + if (!fileRuntime.HasValue) + { + _logger.Error("Failed to get runtime from the file, make sure ffprobe is available"); + return DetectSampleResult.Indeterminate; + } + + return IsSample(path, fileRuntime.Value, series.Runtime); + } + + public DetectSampleResult IsSample(LocalEpisode localEpisode) + { + var extensionResult = IsSample(localEpisode.Path, localEpisode.IsSpecial); + + if (extensionResult != DetectSampleResult.Indeterminate) + { + return extensionResult; + } + + var runtime = 0; + + foreach (var episode in localEpisode.Episodes) + { + runtime += episode.Runtime > 0 ? episode.Runtime : localEpisode.Series.Runtime; + } + + if (localEpisode.MediaInfo == null) + { + _logger.Error("Failed to get runtime from the file, make sure ffprobe is available"); + return DetectSampleResult.Indeterminate; + } + + return IsSample(localEpisode.Path, localEpisode.MediaInfo.RunTime, runtime); + } + + private DetectSampleResult IsSample(string path, bool isSpecial) { if (isSpecial) { @@ -44,49 +91,45 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport return DetectSampleResult.NotSample; } - // TODO: Use MediaInfo from the import process, no need to re-process the file again here - var runTime = _videoFileInfoReader.GetRunTime(path); + return DetectSampleResult.Indeterminate; + } - if (!runTime.HasValue) - { - _logger.Error("Failed to get runtime from the file, make sure ffprobe is available"); - return DetectSampleResult.Indeterminate; - } + private DetectSampleResult IsSample(string path, TimeSpan fileRuntime, int expectedRuntime) + { + var minimumRuntime = GetMinimumAllowedRuntime(expectedRuntime); - var minimumRuntime = GetMinimumAllowedRuntime(series); - - if (runTime.Value.TotalMinutes.Equals(0)) + if (fileRuntime.TotalMinutes.Equals(0)) { _logger.Error("[{0}] has a runtime of 0, is it a valid video file?", path); return DetectSampleResult.Sample; } - if (runTime.Value.TotalSeconds < minimumRuntime) + if (fileRuntime.TotalSeconds < minimumRuntime) { - _logger.Debug("[{0}] appears to be a sample. Runtime: {1} seconds. Expected at least: {2} seconds", path, runTime, minimumRuntime); + _logger.Debug("[{0}] appears to be a sample. Runtime: {1} seconds. Expected at least: {2} seconds", path, fileRuntime, minimumRuntime); return DetectSampleResult.Sample; } - _logger.Debug("[{0}] does not appear to be a sample. Runtime {1} seconds is more than minimum of {2} seconds", path, runTime, minimumRuntime); + _logger.Debug("[{0}] does not appear to be a sample. Runtime {1} seconds is more than minimum of {2} seconds", path, fileRuntime, minimumRuntime); return DetectSampleResult.NotSample; } - private int GetMinimumAllowedRuntime(Series series) + private int GetMinimumAllowedRuntime(int runtime) { // Anime short - 15 seconds - if (series.Runtime <= 3) + if (runtime <= 3) { return 15; } // Webisodes - 90 seconds - if (series.Runtime <= 10) + if (runtime <= 10) { return 90; } // 30 minute episodes - 5 minutes - if (series.Runtime <= 30) + if (runtime <= 30) { return 300; } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs index 7c2b2169e..5d748f0f1 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications try { - var sample = _detectSample.IsSample(localEpisode.Series, localEpisode.Path, localEpisode.IsSpecial); + var sample = _detectSample.IsSample(localEpisode); if (sample == DetectSampleResult.Sample) {