1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2024-12-16 11:37:58 +02:00

Fixed: Improve sample rejection message when MediaInfo is not available

Closes #1967
This commit is contained in:
Mark McDowall 2017-06-10 18:07:24 -07:00 committed by Mark McDowall
parent eea3419849
commit 8cf028e071
12 changed files with 84 additions and 132 deletions

View File

@ -164,11 +164,9 @@ public void should_delete_folder_if_files_were_imported_and_only_sample_files_re
Mocker.GetMock<IDetectSample>() Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(It.IsAny<Series>(), .Setup(s => s.IsSample(It.IsAny<Series>(),
It.IsAny<QualityModel>(),
It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<long>(),
It.IsAny<bool>())) It.IsAny<bool>()))
.Returns(true); .Returns(DetectSampleResult.Sample);
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory)); Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
@ -236,11 +234,9 @@ public void should_not_delete_if_there_is_large_rar_file()
Mocker.GetMock<IDetectSample>() Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(It.IsAny<Series>(), .Setup(s => s.IsSample(It.IsAny<Series>(),
It.IsAny<QualityModel>(),
It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<long>(),
It.IsAny<bool>())) It.IsAny<bool>()))
.Returns(true); .Returns(DetectSampleResult.Sample);
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories)) .Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
@ -347,11 +343,9 @@ public void should_not_delete_if_no_files_were_imported()
Mocker.GetMock<IDetectSample>() Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(It.IsAny<Series>(), .Setup(s => s.IsSample(It.IsAny<Series>(),
It.IsAny<QualityModel>(),
It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<long>(),
It.IsAny<bool>())) It.IsAny<bool>()))
.Returns(true); .Returns(DetectSampleResult.Sample);
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFileSize(It.IsAny<string>())) .Setup(s => s.GetFileSize(It.IsAny<string>()))

View File

@ -14,7 +14,7 @@
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{ {
[TestFixture] [TestFixture]
public class SampleServiceFixture : CoreTest<DetectSample> public class DetectSampleFixture : CoreTest<DetectSample>
{ {
private Series _series; private Series _series;
private LocalEpisode _localEpisode; private LocalEpisode _localEpisode;
@ -42,11 +42,6 @@ public void Setup()
}; };
} }
private void GivenFileSize(long size)
{
_localEpisode.Size = size;
}
private void GivenRuntime(int seconds) private void GivenRuntime(int seconds)
{ {
Mocker.GetMock<IVideoFileInfoReader>() Mocker.GetMock<IVideoFileInfoReader>()
@ -58,7 +53,7 @@ private void GivenRuntime(int seconds)
public void should_return_false_if_season_zero() public void should_return_false_if_season_zero()
{ {
_localEpisode.Episodes[0].SeasonNumber = 0; _localEpisode.Episodes[0].SeasonNumber = 0;
ShouldBeFalse(); ShouldBeNotSample();
} }
[Test] [Test]
@ -66,7 +61,7 @@ public void should_return_false_for_flv()
{ {
_localEpisode.Path = @"C:\Test\some.show.s01e01.flv"; _localEpisode.Path = @"C:\Test\some.show.s01e01.flv";
ShouldBeFalse(); ShouldBeNotSample();
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never()); Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
} }
@ -76,7 +71,7 @@ public void should_return_false_for_strm()
{ {
_localEpisode.Path = @"C:\Test\some.show.s01e01.strm"; _localEpisode.Path = @"C:\Test\some.show.s01e01.strm";
ShouldBeFalse(); ShouldBeNotSample();
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never()); Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
} }
@ -85,12 +80,9 @@ public void should_return_false_for_strm()
public void should_use_runtime() public void should_use_runtime()
{ {
GivenRuntime(120); GivenRuntime(120);
GivenFileSize(1000.Megabytes());
Subject.IsSample(_localEpisode.Series, Subject.IsSample(_localEpisode.Series,
_localEpisode.Quality,
_localEpisode.Path, _localEpisode.Path,
_localEpisode.Size,
_localEpisode.IsSpecial); _localEpisode.IsSpecial);
Mocker.GetMock<IVideoFileInfoReader>().Verify(v => v.GetRunTime(It.IsAny<string>()), Times.Once()); Mocker.GetMock<IVideoFileInfoReader>().Verify(v => v.GetRunTime(It.IsAny<string>()), Times.Once());
@ -101,7 +93,7 @@ public void should_return_true_if_runtime_is_less_than_minimum()
{ {
GivenRuntime(60); GivenRuntime(60);
ShouldBeTrue(); ShouldBeSample();
} }
[Test] [Test]
@ -109,7 +101,7 @@ public void should_return_false_if_runtime_greater_than_minimum()
{ {
GivenRuntime(600); GivenRuntime(600);
ShouldBeFalse(); ShouldBeNotSample();
} }
[Test] [Test]
@ -118,7 +110,7 @@ public void should_return_false_if_runtime_greater_than_webisode_minimum()
_series.Runtime = 6; _series.Runtime = 6;
GivenRuntime(299); GivenRuntime(299);
ShouldBeFalse(); ShouldBeNotSample();
} }
[Test] [Test]
@ -127,7 +119,7 @@ public void should_return_false_if_runtime_greater_than_anime_short_minimum()
_series.Runtime = 2; _series.Runtime = 2;
GivenRuntime(60); GivenRuntime(60);
ShouldBeFalse(); ShouldBeNotSample();
} }
[Test] [Test]
@ -136,29 +128,19 @@ public void should_return_true_if_runtime_less_than_anime_short_minimum()
_series.Runtime = 2; _series.Runtime = 2;
GivenRuntime(10); GivenRuntime(10);
ShouldBeTrue(); ShouldBeSample();
} }
[Test] [Test]
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_acceptable_size() public void should_return_indeterminate_if_mediainfo_result_is_null()
{ {
Mocker.GetMock<IVideoFileInfoReader>() Mocker.GetMock<IVideoFileInfoReader>()
.Setup(s => s.GetRunTime(It.IsAny<string>())) .Setup(s => s.GetRunTime(It.IsAny<string>()))
.Throws<DllNotFoundException>(); .Throws<DllNotFoundException>();
GivenFileSize(1000.Megabytes()); Subject.IsSample(_localEpisode.Series,
ShouldBeFalse(); _localEpisode.Path,
} _localEpisode.IsSpecial).Should().Be(DetectSampleResult.Indeterminate);
[Test]
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_undersize()
{
Mocker.GetMock<IVideoFileInfoReader>()
.Setup(s => s.GetRunTime(It.IsAny<string>()))
.Throws<DllNotFoundException>();
GivenFileSize(1.Megabytes());
ShouldBeTrue();
} }
[Test] [Test]
@ -167,7 +149,7 @@ public void should_not_treat_daily_episode_a_special()
GivenRuntime(600); GivenRuntime(600);
_series.SeriesType = SeriesTypes.Daily; _series.SeriesType = SeriesTypes.Daily;
_localEpisode.Episodes[0].SeasonNumber = 0; _localEpisode.Episodes[0].SeasonNumber = 0;
ShouldBeFalse(); ShouldBeNotSample();
} }
[Test] [Test]
@ -176,25 +158,21 @@ public void should_return_false_for_anime_special()
_series.SeriesType = SeriesTypes.Anime; _series.SeriesType = SeriesTypes.Anime;
_localEpisode.Episodes[0].SeasonNumber = 0; _localEpisode.Episodes[0].SeasonNumber = 0;
ShouldBeFalse(); ShouldBeNotSample();
} }
private void ShouldBeTrue() private void ShouldBeSample()
{ {
Subject.IsSample(_localEpisode.Series, Subject.IsSample(_localEpisode.Series,
_localEpisode.Quality,
_localEpisode.Path, _localEpisode.Path,
_localEpisode.Size, _localEpisode.IsSpecial).Should().Be(DetectSampleResult.Sample);
_localEpisode.IsSpecial).Should().BeTrue();
} }
private void ShouldBeFalse() private void ShouldBeNotSample()
{ {
Subject.IsSample(_localEpisode.Series, Subject.IsSample(_localEpisode.Series,
_localEpisode.Quality,
_localEpisode.Path, _localEpisode.Path,
_localEpisode.Size, _localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample);
_localEpisode.IsSpecial).Should().BeFalse();
} }
} }
} }

View File

@ -333,8 +333,16 @@ public void should_use_folder_when_only_one_video_file_and_a_sample()
GivenVideoFiles(videoFiles.ToList()); GivenVideoFiles(videoFiles.ToList());
Mocker.GetMock<IDetectSample>() Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(_series, It.IsAny<QualityModel>(), It.Is<string>(c => c.Contains("sample")), It.IsAny<long>(), It.IsAny<bool>())) .Setup(s => s.IsSample(_series, It.IsAny<string>(), It.IsAny<bool>()))
.Returns(true); .Returns((Series s, string path, bool special) =>
{
if (path.Contains("sample"))
{
return DetectSampleResult.Sample;
}
return DetectSampleResult.NotSample;
});
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01"); var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");

View File

@ -1,4 +1,4 @@
using System.IO; using System.IO;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -30,11 +30,9 @@ public void get_runtime()
{ {
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4"); var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4");
Subject.GetRunTime(path).Seconds.Should().Be(10); Subject.GetRunTime(path).Value.Seconds.Should().Be(10);
} }
[Test] [Test]
public void get_info() public void get_info()
{ {
@ -86,7 +84,6 @@ public void get_info_unicode()
info.VideoCodec.Should().Be("AVC"); info.VideoCodec.Should().Be("AVC");
info.VideoFps.Should().Be(24); info.VideoFps.Should().Be(24);
info.Width.Should().Be(480); info.Width.Should().Be(480);
} }
[Test] [Test]

View File

@ -284,7 +284,7 @@
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" /> <Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" /> <Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" /> <Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\SampleServiceFixture.cs" /> <Compile Include="MediaFiles\EpisodeImport\DetectSampleFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" /> <Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecificationFixture.cs" /> <Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\SameFileSpecificationFixture.cs" /> <Compile Include="MediaFiles\EpisodeImport\Specifications\SameFileSpecificationFixture.cs" />

View File

@ -113,10 +113,7 @@ public bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Series series)
return false; return false;
} }
var size = _diskProvider.GetFileSize(videoFile); if (_detectSample.IsSample(series, videoFile, episodeParseResult.IsPossibleSpecialEpisode) != DetectSampleResult.Sample)
var quality = QualityParser.ParseQuality(videoFile);
if (!_detectSample.IsSample(series, quality, videoFile, size, episodeParseResult.IsPossibleSpecialEpisode))
{ {
_logger.Warn("Non-sample file detected: [{0}]", videoFile); _logger.Warn("Non-sample file detected: [{0}]", videoFile);
return false; return false;

View File

@ -1,16 +1,14 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using NLog; using NLog;
using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MediaFiles.EpisodeImport namespace NzbDrone.Core.MediaFiles.EpisodeImport
{ {
public interface IDetectSample public interface IDetectSample
{ {
bool IsSample(Series series, QualityModel quality, string path, long size, bool isSpecial); DetectSampleResult IsSample(Series series, string path, bool isSpecial);
} }
public class DetectSample : IDetectSample public class DetectSample : IDetectSample
@ -18,22 +16,18 @@ public class DetectSample : IDetectSample
private readonly IVideoFileInfoReader _videoFileInfoReader; private readonly IVideoFileInfoReader _videoFileInfoReader;
private readonly Logger _logger; private readonly Logger _logger;
private static List<Quality> _largeSampleSizeQualities = new List<Quality> { Quality.HDTV1080p, Quality.WEBDL1080p, Quality.Bluray1080p };
public DetectSample(IVideoFileInfoReader videoFileInfoReader, Logger logger) public DetectSample(IVideoFileInfoReader videoFileInfoReader, Logger logger)
{ {
_videoFileInfoReader = videoFileInfoReader; _videoFileInfoReader = videoFileInfoReader;
_logger = logger; _logger = logger;
} }
public static long SampleSizeLimit => 70.Megabytes(); public DetectSampleResult IsSample(Series series, string path, bool isSpecial)
public bool IsSample(Series series, QualityModel quality, string path, long size, bool isSpecial)
{ {
if (isSpecial) if (isSpecial)
{ {
_logger.Debug("Special, skipping sample check"); _logger.Debug("Special, skipping sample check");
return false; return DetectSampleResult.NotSample;
} }
var extension = Path.GetExtension(path); var extension = Path.GetExtension(path);
@ -41,61 +35,39 @@ public bool IsSample(Series series, QualityModel quality, string path, long size
if (extension != null && extension.Equals(".flv", StringComparison.InvariantCultureIgnoreCase)) if (extension != null && extension.Equals(".flv", StringComparison.InvariantCultureIgnoreCase))
{ {
_logger.Debug("Skipping sample check for .flv file"); _logger.Debug("Skipping sample check for .flv file");
return false; return DetectSampleResult.NotSample;
} }
if (extension != null && extension.Equals(".strm", StringComparison.InvariantCultureIgnoreCase)) if (extension != null && extension.Equals(".strm", StringComparison.InvariantCultureIgnoreCase))
{ {
_logger.Debug("Skipping sample check for .strm file"); _logger.Debug("Skipping sample check for .strm file");
return false; return DetectSampleResult.NotSample;
} }
try
{
var runTime = _videoFileInfoReader.GetRunTime(path); var runTime = _videoFileInfoReader.GetRunTime(path);
if (!runTime.HasValue)
{
_logger.Error("Failed to get runtime from the file, make sure mediainfo is available");
return DetectSampleResult.Indeterminate;
}
var minimumRuntime = GetMinimumAllowedRuntime(series); var minimumRuntime = GetMinimumAllowedRuntime(series);
if (runTime.TotalMinutes.Equals(0)) if (runTime.Value.TotalMinutes.Equals(0))
{ {
_logger.Error("[{0}] has a runtime of 0, is it a valid video file?", path); _logger.Error("[{0}] has a runtime of 0, is it a valid video file?", path);
return true; return DetectSampleResult.Sample;
} }
if (runTime.TotalSeconds < minimumRuntime) if (runTime.Value.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, runTime, minimumRuntime);
return true; return DetectSampleResult.Sample;
}
}
catch (DllNotFoundException)
{
_logger.Debug("Falling back to file size detection");
return CheckSize(size, quality);
} }
_logger.Debug("Runtime is over 90 seconds"); _logger.Debug("Runtime is over 90 seconds");
return false; return DetectSampleResult.NotSample;
}
private bool CheckSize(long size, QualityModel quality)
{
{
if (size < SampleSizeLimit * 2)
{
_logger.Debug("1080p file is less than sample limit");
return true;
}
}
if (size < SampleSizeLimit)
{
_logger.Debug("File is less than sample limit");
return true;
}
return false;
} }
private int GetMinimumAllowedRuntime(Series series) private int GetMinimumAllowedRuntime(Series series)

View File

@ -0,0 +1,9 @@
namespace NzbDrone.Core.MediaFiles.EpisodeImport
{
public enum DetectSampleResult
{
Indeterminate,
Sample,
NotSample
}
}

View File

@ -170,11 +170,9 @@ private bool ShouldUseFolderName(List<string> videoFiles, Series series, ParsedE
return videoFiles.Count(file => return videoFiles.Count(file =>
{ {
var size = _diskProvider.GetFileSize(file); var sample = _detectSample.IsSample(series, file, folderInfo.IsPossibleSpecialEpisode);
var fileQuality = QualityParser.ParseQuality(file);
var sample = _detectSample.IsSample(series, GetQuality(folderInfo, fileQuality, series), file, size, folderInfo.IsPossibleSpecialEpisode);
if (sample) if (sample == DetectSampleResult.Sample)
{ {
return false; return false;
} }

View File

@ -26,16 +26,19 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down
} }
var sample = _detectSample.IsSample(localEpisode.Series, var sample = _detectSample.IsSample(localEpisode.Series,
localEpisode.Quality,
localEpisode.Path, localEpisode.Path,
localEpisode.Size,
localEpisode.IsSpecial); localEpisode.IsSpecial);
if (sample) if (sample == DetectSampleResult.Sample)
{ {
return Decision.Reject("Sample"); return Decision.Reject("Sample");
} }
else if (sample == DetectSampleResult.Indeterminate)
{
return Decision.Reject("Unable to determine if file is a sample");
}
return Decision.Accept(); return Decision.Accept();
} }
} }

View File

@ -9,7 +9,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
public interface IVideoFileInfoReader public interface IVideoFileInfoReader
{ {
MediaInfoModel GetMediaInfo(string filename); MediaInfoModel GetMediaInfo(string filename);
TimeSpan GetRunTime(string filename); TimeSpan? GetRunTime(string filename);
} }
public class VideoFileInfoReader : IVideoFileInfoReader public class VideoFileInfoReader : IVideoFileInfoReader
@ -181,16 +181,11 @@ public MediaInfoModel GetMediaInfo(string filename)
return null; return null;
} }
public TimeSpan GetRunTime(string filename) public TimeSpan? GetRunTime(string filename)
{ {
var info = GetMediaInfo(filename); var info = GetMediaInfo(filename);
if (info == null) return info?.RunTime;
{
return new TimeSpan();
}
return info.RunTime;
} }
private TimeSpan GetBestRuntime(int audio, int video, int general) private TimeSpan GetBestRuntime(int audio, int video, int general)

View File

@ -745,6 +745,7 @@
<Compile Include="MediaFiles\Commands\BackendCommandAttribute.cs" /> <Compile Include="MediaFiles\Commands\BackendCommandAttribute.cs" />
<Compile Include="MediaFiles\Commands\CleanUpRecycleBinCommand.cs" /> <Compile Include="MediaFiles\Commands\CleanUpRecycleBinCommand.cs" />
<Compile Include="MediaFiles\Commands\DownloadedEpisodesScanCommand.cs" /> <Compile Include="MediaFiles\Commands\DownloadedEpisodesScanCommand.cs" />
<Compile Include="MediaFiles\EpisodeImport\DetectSampleResult.cs" />
<Compile Include="MediaFiles\EpisodeImport\ImportMode.cs" /> <Compile Include="MediaFiles\EpisodeImport\ImportMode.cs" />
<Compile Include="MediaFiles\Commands\RenameFilesCommand.cs" /> <Compile Include="MediaFiles\Commands\RenameFilesCommand.cs" />
<Compile Include="MediaFiles\Commands\RenameSeriesCommand.cs" /> <Compile Include="MediaFiles\Commands\RenameSeriesCommand.cs" />