mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-04 10:02:40 +01:00
Episode import improvements
Fixed: Use folder name when file name is not parsable on import
This commit is contained in:
parent
5b54b02d7e
commit
20782bbbc1
@ -1,6 +1,6 @@
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Test.Common
|
||||
namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
public static class ObjectExtensions
|
||||
{
|
@ -134,6 +134,7 @@
|
||||
<Compile Include="Extensions\DateTimeExtensions.cs" />
|
||||
<Compile Include="Crypto\HashConverter.cs" />
|
||||
<Compile Include="Extensions\Int64Extensions.cs" />
|
||||
<Compile Include="Extensions\ObjectExtensions.cs" />
|
||||
<Compile Include="Extensions\StreamExtensions.cs" />
|
||||
<Compile Include="Extensions\XmlExtentions.cs" />
|
||||
<Compile Include="HashUtil.cs" />
|
||||
|
@ -5,6 +5,7 @@
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.History;
|
||||
@ -153,8 +154,13 @@ public void should_not_mark_as_imported_if_all_files_were_rejected()
|
||||
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>
|
||||
{
|
||||
new ImportResult(new ImportDecision(new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"}, "Rejected!"),"Test Failure"),
|
||||
new ImportResult(new ImportDecision(new LocalEpisode {Path = @"C:\TestPath\Droned.S01E02.mkv"}, "Rejected!"),"Test Failure")
|
||||
new ImportResult(
|
||||
new ImportDecision(
|
||||
new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"}, new Rejection("Rejected!")), "Test Failure"),
|
||||
|
||||
new ImportResult(
|
||||
new ImportDecision(
|
||||
new LocalEpisode {Path = @"C:\TestPath\Droned.S01E02.mkv"},new Rejection("Rejected!")), "Test Failure")
|
||||
});
|
||||
|
||||
Subject.Process(_trackedDownload);
|
||||
|
@ -4,6 +4,7 @@
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Parser;
|
||||
@ -12,7 +13,6 @@
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
|
@ -4,17 +4,16 @@
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
@ -14,7 +15,6 @@
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
|
@ -101,7 +101,7 @@ public void should_not_scan_extras_subfolder()
|
||||
Subject.Scan(_series);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _series, false, (QualityModel)null), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _series), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -119,7 +119,7 @@ public void should_not_scan_AppleDouble_subfolder()
|
||||
Subject.Scan(_series);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _series, false, (QualityModel)null), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _series), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -141,7 +141,7 @@ public void should_scan_extras_series_and_subfolders()
|
||||
Subject.Scan(_series);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 4), _series, false, (QualityModel)null), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 4), _series), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -160,7 +160,7 @@ public void should_not_scan_subfolders_that_start_with_period()
|
||||
Subject.Scan(_series);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _series, false, (QualityModel)null), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _series), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ public void should_skip_if_no_series_found()
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(c => c.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<bool>(), It.IsAny<QualityModel>()),
|
||||
.Verify(c => c.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()),
|
||||
Times.Never());
|
||||
|
||||
VerifyNoImport();
|
||||
@ -129,7 +129,7 @@ public void should_not_delete_folder_if_files_were_imported_and_video_files_rema
|
||||
imported.Add(new ImportDecision(localEpisode));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<String>>(), It.IsAny<Series>(), true, null))
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<String>>(), It.IsAny<Series>(), null, true))
|
||||
.Returns(imported);
|
||||
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
@ -155,14 +155,14 @@ public void should_delete_folder_if_files_were_imported_and_only_sample_files_re
|
||||
imported.Add(new ImportDecision(localEpisode));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<String>>(), It.IsAny<Series>(), true, null))
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<String>>(), It.IsAny<Series>(), null, true))
|
||||
.Returns(imported);
|
||||
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null))
|
||||
.Returns(imported.Select(i => new ImportResult(i)).ToList());
|
||||
|
||||
Mocker.GetMock<ISampleService>()
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(It.IsAny<Series>(),
|
||||
It.IsAny<QualityModel>(),
|
||||
It.IsAny<String>(),
|
||||
@ -224,14 +224,14 @@ public void should_not_delete_if_there_is_large_rar_file()
|
||||
imported.Add(new ImportDecision(localEpisode));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<String>>(), It.IsAny<Series>(), true, null))
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<String>>(), It.IsAny<Series>(), null, true))
|
||||
.Returns(imported);
|
||||
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null))
|
||||
.Returns(imported.Select(i => new ImportResult(i)).ToList());
|
||||
|
||||
Mocker.GetMock<ISampleService>()
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(It.IsAny<Series>(),
|
||||
It.IsAny<QualityModel>(),
|
||||
It.IsAny<String>(),
|
||||
|
@ -45,31 +45,20 @@ public void Setup()
|
||||
_fail2 = new Mock<IImportDecisionEngineSpecification>();
|
||||
_fail3 = new Mock<IImportDecisionEngineSpecification>();
|
||||
|
||||
_pass1.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(true);
|
||||
_pass1.Setup(c => c.RejectionReason).Returns("_pass1");
|
||||
_pass1.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Accept());
|
||||
_pass2.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Accept());
|
||||
_pass3.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Accept());
|
||||
|
||||
_pass2.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(true);
|
||||
_pass2.Setup(c => c.RejectionReason).Returns("_pass2");
|
||||
_fail1.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Reject("_fail1"));
|
||||
_fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Reject("_fail2"));
|
||||
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Reject("_fail3"));
|
||||
|
||||
_pass3.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(true);
|
||||
_pass3.Setup(c => c.RejectionReason).Returns("_pass3");
|
||||
|
||||
|
||||
_fail1.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(false);
|
||||
_fail1.Setup(c => c.RejectionReason).Returns("_fail1");
|
||||
|
||||
_fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(false);
|
||||
_fail2.Setup(c => c.RejectionReason).Returns("_fail2");
|
||||
|
||||
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(false);
|
||||
_fail3.Setup(c => c.RejectionReason).Returns("_fail3");
|
||||
|
||||
_videoFiles = new List<string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" };
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
_quality = new QualityModel(Quality.DVD);
|
||||
|
||||
_localEpisode = new LocalEpisode
|
||||
{
|
||||
Series = _series,
|
||||
@ -78,17 +67,24 @@ public void Setup()
|
||||
};
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<Boolean>()))
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Boolean>()))
|
||||
.Returns(_localEpisode);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<Series>()))
|
||||
.Returns(_videoFiles);
|
||||
GivenVideoFiles(new List<string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi".AsOsAgnostic() });
|
||||
}
|
||||
|
||||
private void GivenSpecifications(params Mock<IImportDecisionEngineSpecification>[] mocks)
|
||||
{
|
||||
Mocker.SetConstant<IEnumerable<IRejectWithReason>>(mocks.Select(c => c.Object));
|
||||
Mocker.SetConstant(mocks.Select(c => c.Object));
|
||||
}
|
||||
|
||||
private void GivenVideoFiles(IEnumerable<string> videoFiles)
|
||||
{
|
||||
_videoFiles = videoFiles.ToList();
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<Series>()))
|
||||
.Returns(_videoFiles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -96,7 +92,7 @@ public void should_call_all_specifications()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, new Series(), false);
|
||||
Subject.GetImportDecisions(_videoFiles, new Series(), null, false);
|
||||
|
||||
_fail1.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
|
||||
_fail2.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
|
||||
@ -111,7 +107,7 @@ public void should_return_rejected_if_single_specs_fail()
|
||||
{
|
||||
GivenSpecifications(_fail1);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false);
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
|
||||
result.Single().Approved.Should().BeFalse();
|
||||
}
|
||||
@ -121,7 +117,7 @@ public void should_return_rejected_if_one_of_specs_fail()
|
||||
{
|
||||
GivenSpecifications(_pass1, _fail1, _pass2, _pass3);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false);
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
|
||||
result.Single().Approved.Should().BeFalse();
|
||||
}
|
||||
@ -131,7 +127,7 @@ public void should_return_pass_if_all_specs_pass()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false);
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
|
||||
result.Single().Approved.Should().BeTrue();
|
||||
}
|
||||
@ -141,7 +137,7 @@ public void should_have_same_number_of_rejections_as_specs_that_failed()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false);
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
result.Single().Rejections.Should().HaveCount(3);
|
||||
}
|
||||
|
||||
@ -151,7 +147,7 @@ public void should_not_blowup_the_process_due_to_failed_parse()
|
||||
GivenSpecifications(_pass1);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<Boolean>()))
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Boolean>()))
|
||||
.Throws<TestException>();
|
||||
|
||||
_videoFiles = new List<String>
|
||||
@ -161,14 +157,12 @@ public void should_not_blowup_the_process_due_to_failed_parse()
|
||||
"The.Office.S03E115.DVDRip.XviD-OSiTV"
|
||||
};
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<Series>()))
|
||||
.Returns(_videoFiles);
|
||||
GivenVideoFiles(_videoFiles);
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, false);
|
||||
Subject.GetImportDecisions(_videoFiles, _series);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<Boolean>()), Times.Exactly(_videoFiles.Count));
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Boolean>()), Times.Exactly(_videoFiles.Count));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(3);
|
||||
}
|
||||
@ -179,7 +173,7 @@ public void should_use_file_quality_if_folder_quality_is_null()
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single());
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series, false, null);
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series);
|
||||
|
||||
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
|
||||
}
|
||||
@ -190,7 +184,7 @@ public void should_use_file_quality_if_folder_quality_is_lower_than_file_quality
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single());
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series, false, new QualityModel(Quality.SDTV));
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series, new ParsedEpisodeInfo{Quality = new QualityModel(Quality.SDTV)}, true);
|
||||
|
||||
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
|
||||
}
|
||||
@ -201,7 +195,7 @@ public void should_use_folder_quality_when_it_is_greater_than_file_quality()
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
var expectedQuality = new QualityModel(Quality.Bluray1080p);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series, false, expectedQuality);
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _series, new ParsedEpisodeInfo { Quality = expectedQuality }, true);
|
||||
|
||||
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
|
||||
}
|
||||
@ -212,7 +206,7 @@ public void should_not_throw_if_episodes_are_not_found()
|
||||
GivenSpecifications(_pass1);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<Boolean>()))
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Boolean>()))
|
||||
.Throws(new EpisodeNotFoundException("Episode not found"));
|
||||
|
||||
_videoFiles = new List<String>
|
||||
@ -222,14 +216,130 @@ public void should_not_throw_if_episodes_are_not_found()
|
||||
"The.Office.S03E115.DVDRip.XviD-OSiTV"
|
||||
};
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<Series>()))
|
||||
.Returns(_videoFiles);
|
||||
GivenVideoFiles(_videoFiles);
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, false);
|
||||
Subject.GetImportDecisions(_videoFiles, _series);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<Boolean>()), Times.Exactly(_videoFiles.Count));
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Boolean>()), Times.Exactly(_videoFiles.Count));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_for_full_season()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01\S01E01.mkv".AsOsAgnostic(),
|
||||
@"C:\Test\Unsorted\Series.Title.S01\S01E02.mkv".AsOsAgnostic(),
|
||||
@"C:\Test\Unsorted\Series.Title.S01\S01E03.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles);
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Exactly(3));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.Is<ParsedEpisodeInfo>(p => p != null), true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_when_it_contains_more_than_one_valid_video_file()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\S01E01.mkv".AsOsAgnostic(),
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\1x01.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles);
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Exactly(2));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.Is<ParsedEpisodeInfo>(p => p != null), true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_when_only_one_video_file()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\S01E01.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles);
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), true), Times.Exactly(1));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_when_only_one_video_file_and_a_sample()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\S01E01.mkv".AsOsAgnostic(),
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01\S01E01.sample.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles.ToList());
|
||||
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(_series, It.IsAny<QualityModel>(), It.Is<string>(c => c.Contains("sample")), It.IsAny<long>(), It.IsAny<int>()))
|
||||
.Returns(true);
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), true), Times.Exactly(2));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_name_if_file_name_is_scene_name()
|
||||
{
|
||||
var videoFiles = new[]
|
||||
{
|
||||
@"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-LOL\Series.Title.S01E01.720p.HDTV-LOL.mkv".AsOsAgnostic()
|
||||
};
|
||||
|
||||
GivenSpecifications(_pass1);
|
||||
GivenVideoFiles(videoFiles);
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01.720p.HDTV-LOL");
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Exactly(1));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.Is<ParsedEpisodeInfo>(p => p != null), true), Times.Never());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
{
|
||||
[TestFixture]
|
||||
public class SampleServiceFixture : CoreTest<SampleService>
|
||||
public class SampleServiceFixture : CoreTest<DetectSample>
|
||||
{
|
||||
private Series _series;
|
||||
private LocalEpisode _localEpisode;
|
||||
|
@ -64,7 +64,7 @@ public void should_reject_when_there_isnt_enough_disk_space()
|
||||
GivenFileSize(100.Megabytes());
|
||||
GivenFreeSpace(80.Megabytes());
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ public void should_reject_when_there_isnt_enough_space_for_file_plus_100mb_paddi
|
||||
GivenFileSize(100.Megabytes());
|
||||
GivenFreeSpace(150.Megabytes());
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ public void should_accept_when_there_is_enough_disk_space()
|
||||
GivenFileSize(100.Megabytes());
|
||||
GivenFreeSpace(1.Gigabytes());
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -93,7 +93,7 @@ public void should_use_series_paths_parent_for_free_space_check()
|
||||
GivenFileSize(100.Megabytes());
|
||||
GivenFreeSpace(1.Gigabytes());
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetAvailableSpace(_rootFolder), Times.Once());
|
||||
@ -105,7 +105,7 @@ public void should_pass_if_free_space_is_null()
|
||||
GivenFileSize(100.Megabytes());
|
||||
GivenFreeSpace(null);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -117,7 +117,7 @@ public void should_pass_if_exception_is_thrown()
|
||||
.Setup(s => s.GetAvailableSpace(It.IsAny<String>()))
|
||||
.Throws(new TestException());
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ public void should_skip_check_for_files_under_series_folder()
|
||||
{
|
||||
_localEpisode.ExistingFile = true;
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(s => s.GetAvailableSpace(It.IsAny<String>()), Times.Never());
|
||||
@ -141,7 +141,7 @@ public void should_return_true_if_free_space_is_null()
|
||||
.Setup(s => s.GetAvailableSpace(It.IsAny<String>()))
|
||||
.Returns(freeSpace);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -151,7 +151,7 @@ public void should_return_true_when_skip_check_is_enabled()
|
||||
.Setup(s => s.SkipFreeSpaceCheckWhenImporting)
|
||||
.Returns(true);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,13 +34,13 @@ public void should_return_false_when_file_contains_the_full_season()
|
||||
{
|
||||
_localEpisode.ParsedEpisodeInfo.FullSeason = true;
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_file_does_not_contain_the_full_season()
|
||||
{
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
[TestFixture]
|
||||
public class MatchesFolderSpecificationFixture : CoreTest<MatchesFolderSpecification>
|
||||
{
|
||||
private LocalEpisode _localEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_localEpisode = Builder<LocalEpisode>.CreateNew()
|
||||
.With(l => l.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E05.mkv".AsOsAgnostic())
|
||||
.With(l => l.ParsedEpisodeInfo =
|
||||
Builder<ParsedEpisodeInfo>.CreateNew()
|
||||
.With(p => p.EpisodeNumbers = new[] {5})
|
||||
.With(p => p.FullSeason = false)
|
||||
.Build())
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_for_existing_file()
|
||||
{
|
||||
_localEpisode.ExistingFile = true;
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_folder_name_is_not_parseable()
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title\S01E01.mkv".AsOsAgnostic();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_should_be_accepted_for_full_season()
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01\S01E01.mkv".AsOsAgnostic();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_file_and_folder_have_the_same_episode()
|
||||
{
|
||||
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 1 };
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E01.mkv".AsOsAgnostic();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_file_is_one_episode_in_folder()
|
||||
{
|
||||
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 1 };
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01E02.720p.HDTV-Sonarr\S01E01.mkv".AsOsAgnostic();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_file_and_folder_do_not_have_same_episode()
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E05.mkv".AsOsAgnostic();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_file_and_folder_do_not_have_same_episodes()
|
||||
{
|
||||
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 5, 6 };
|
||||
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01E02.720p.HDTV-Sonarr\S01E05E06.mkv".AsOsAgnostic();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ public void Setup()
|
||||
public void should_return_true_for_existing_file()
|
||||
{
|
||||
_localEpisode.ExistingFile = true;
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ private void GivenLastWriteTimeUtc(DateTime time)
|
||||
[Test]
|
||||
public void should_return_true_if_not_in_working_folder()
|
||||
{
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -59,7 +59,7 @@ public void should_return_true_when_in_old_working_folder()
|
||||
GivenInWorkingFolder();
|
||||
GivenLastWriteTimeUtc(DateTime.UtcNow.AddHours(-1));
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -68,7 +68,7 @@ public void should_return_false_if_in_working_folder_and_last_write_time_was_rec
|
||||
GivenInWorkingFolder();
|
||||
GivenLastWriteTimeUtc(DateTime.UtcNow);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -79,7 +79,7 @@ public void should_return_false_if_unopacking_on_linux()
|
||||
GivenInWorkingFolder();
|
||||
GivenLastWriteTimeUtc(DateTime.UtcNow.AddDays(-5));
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public void should_return_true_if_no_existing_episodeFile()
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -58,7 +58,7 @@ public void should_return_true_if_no_existing_episodeFile_for_multi_episodes()
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -75,7 +75,7 @@ public void should_return_true_if_upgrade_for_existing_episodeFile()
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -92,7 +92,7 @@ public void should_return_true_if_upgrade_for_existing_episodeFile_for_multi_epi
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -109,7 +109,7 @@ public void should_return_false_if_not_an_upgrade_for_existing_episodeFile()
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -126,7 +126,7 @@ public void should_return_false_if_not_an_upgrade_for_existing_episodeFile_for_m
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -150,7 +150,7 @@ public void should_return_false_if_not_an_upgrade_for_one_existing_episodeFile_f
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
@ -44,9 +45,9 @@ public void Setup()
|
||||
|
||||
|
||||
|
||||
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), "Rejected!"));
|
||||
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), "Rejected!"));
|
||||
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), "Rejected!"));
|
||||
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new Rejection("Rejected!")));
|
||||
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new Rejection("Rejected!")));
|
||||
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new Rejection("Rejected!")));
|
||||
|
||||
foreach (var episode in episodes)
|
||||
{
|
||||
|
@ -227,6 +227,7 @@
|
||||
<Compile Include="MediaFiles\EpisodeImport\SampleServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\MatchesFolderSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecificationFixture.cs" />
|
||||
|
@ -29,6 +29,7 @@ public class CrapParserFixture : CoreTest
|
||||
[TestCase("08bbc153931ce3ca5fcafe1b92d3297285feb061.mkv")]
|
||||
[TestCase("185d86a343e39f3341e35c4dad3ff159")]
|
||||
[TestCase("ah63jka93jf0jh26ahjas961.mkv")]
|
||||
[TestCase("qrdSD3rYzWb7cPdVIGSn4E7")]
|
||||
public void should_not_parse_crap(string title)
|
||||
{
|
||||
Parser.Parser.ParseTitle(title).Should().BeNull();
|
||||
@ -82,5 +83,11 @@ public void should_not_parse_random(int length)
|
||||
|
||||
success.Should().Be(repetitions);
|
||||
}
|
||||
|
||||
[TestCase("thebiggestloser1618finale")]
|
||||
public void should_not_parse_file_name_without_proper_spacing(string fileName)
|
||||
{
|
||||
Parser.Parser.ParseTitle(fileName).Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,10 @@
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests
|
||||
{
|
||||
|
@ -4,11 +4,11 @@
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Commands;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ public void Scan(Series series)
|
||||
_logger.Trace("Finished getting episode files for: {0} [{1}]", series, videoFilesStopwatch.Elapsed);
|
||||
|
||||
var decisionsStopwatch = Stopwatch.StartNew();
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, series, false);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, series);
|
||||
decisionsStopwatch.Stop();
|
||||
_logger.Trace("Import decisions complete for: {0} [{1}]", series, decisionsStopwatch.Elapsed);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -26,7 +27,7 @@ public class DownloadedEpisodesImportService : IDownloadedEpisodesImportService
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IMakeImportDecision _importDecisionMaker;
|
||||
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
||||
private readonly ISampleService _sampleService;
|
||||
private readonly IDetectSample _detectSample;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
@ -35,7 +36,7 @@ public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
IParsingService parsingService,
|
||||
IMakeImportDecision importDecisionMaker,
|
||||
IImportApprovedEpisodes importApprovedEpisodes,
|
||||
ISampleService sampleService,
|
||||
IDetectSample detectSample,
|
||||
Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
@ -44,7 +45,7 @@ public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
_parsingService = parsingService;
|
||||
_importDecisionMaker = importDecisionMaker;
|
||||
_importApprovedEpisodes = importApprovedEpisodes;
|
||||
_sampleService = sampleService;
|
||||
_detectSample = detectSample;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -115,9 +116,12 @@ private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, Series ser
|
||||
}
|
||||
|
||||
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
|
||||
var quality = QualityParser.ParseQuality(cleanedUpName);
|
||||
var folderInfo = Parser.Parser.ParseTitle(directoryInfo.Name);
|
||||
|
||||
_logger.Debug("{0} folder quality: {1}", cleanedUpName, quality);
|
||||
if (folderInfo != null)
|
||||
{
|
||||
_logger.Debug("{0} folder quality: {1}", cleanedUpName, folderInfo.Quality);
|
||||
}
|
||||
|
||||
var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName);
|
||||
|
||||
@ -135,7 +139,7 @@ private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, Series ser
|
||||
}
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), series, true, quality);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), series, folderInfo, true);
|
||||
var importResults = _importApprovedEpisodes.Import(decisions, true, downloadClientItem);
|
||||
|
||||
if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) && importResults.Any() && ShouldDeleteFolder(directoryInfo, series))
|
||||
@ -177,7 +181,9 @@ private List<ImportResult> ProcessFile(FileInfo fileInfo, Series series, Downloa
|
||||
}
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, series, true);
|
||||
var folderInfo = Parser.Parser.ParseTitle(fileInfo.DirectoryName);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, series, folderInfo, true);
|
||||
|
||||
return _importApprovedEpisodes.Import(decisions, true, downloadClientItem);
|
||||
}
|
||||
|
||||
@ -207,7 +213,7 @@ private bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Series series)
|
||||
var size = _diskProvider.GetFileSize(videoFile);
|
||||
var quality = QualityParser.ParseQuality(videoFile);
|
||||
|
||||
if (!_sampleService.IsSample(series, quality, videoFile, size,
|
||||
if (!_detectSample.IsSample(series, quality, videoFile, size,
|
||||
episodeParseResult.SeasonNumber))
|
||||
{
|
||||
_logger.Warn("Non-sample file detected: [{0}]", videoFile);
|
||||
@ -227,14 +233,14 @@ private bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Series series)
|
||||
private ImportResult FileIsLockedResult(string videoFile)
|
||||
{
|
||||
_logger.Debug("[{0}] is currently locked by another process, skipping", videoFile);
|
||||
return new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, "Locked file, try again later"), "Locked file, try again later");
|
||||
return new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, new Rejection("Locked file, try again later")), "Locked file, try again later");
|
||||
}
|
||||
|
||||
private ImportResult UnknownSeriesResult(string message, string videoFile = null)
|
||||
{
|
||||
var localEpisode = videoFile == null ? null : new LocalEpisode { Path = videoFile };
|
||||
|
||||
return new ImportResult(new ImportDecision(localEpisode, "Unknown Series"), message);
|
||||
return new ImportResult(new ImportDecision(localEpisode, new Rejection("Unknown Series")), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,19 +8,19 @@
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public interface ISampleService
|
||||
public interface IDetectSample
|
||||
{
|
||||
bool IsSample(Series series, QualityModel quality, string path, long size, int seasonNumber);
|
||||
}
|
||||
|
||||
public class SampleService : ISampleService
|
||||
public class DetectSample : IDetectSample
|
||||
{
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private static List<Quality> _largeSampleSizeQualities = new List<Quality> { Quality.HDTV1080p, Quality.WEBDL1080p, Quality.Bluray1080p };
|
||||
|
||||
public SampleService(IVideoFileInfoReader videoFileInfoReader, Logger logger)
|
||||
public DetectSample(IVideoFileInfoReader videoFileInfoReader, Logger logger)
|
||||
{
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_logger = logger;
|
@ -3,8 +3,8 @@
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public interface IImportDecisionEngineSpecification : IRejectWithReason
|
||||
public interface IImportDecisionEngineSpecification
|
||||
{
|
||||
bool IsSatisfiedBy(LocalEpisode localEpisode);
|
||||
Decision IsSatisfiedBy(LocalEpisode localEpisode);
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownloa
|
||||
|
||||
//Adding all the rejected decisions
|
||||
importResults.AddRange(decisions.Where(c => !c.Approved)
|
||||
.Select(d => new ImportResult(d, d.Rejections.ToArray())));
|
||||
.Select(d => new ImportResult(d, d.Rejections.Select(r => r.Reason).ToArray())));
|
||||
|
||||
return importResults;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
@ -8,7 +9,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
public class ImportDecision
|
||||
{
|
||||
public LocalEpisode LocalEpisode { get; private set; }
|
||||
public IEnumerable<string> Rejections { get; private set; }
|
||||
public IEnumerable<Rejection> Rejections { get; private set; }
|
||||
|
||||
public bool Approved
|
||||
{
|
||||
@ -18,7 +19,7 @@ public bool Approved
|
||||
}
|
||||
}
|
||||
|
||||
public ImportDecision(LocalEpisode localEpisode, params string[] rejections)
|
||||
public ImportDecision(LocalEpisode localEpisode, params Rejection[] rejections)
|
||||
{
|
||||
LocalEpisode = localEpisode;
|
||||
Rejections = rejections.ToList();
|
||||
|
@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@ -15,23 +17,26 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public interface IMakeImportDecision
|
||||
{
|
||||
List<ImportDecision> GetImportDecisions(List<String> videoFiles, Series series, bool sceneSource, QualityModel quality = null);
|
||||
List<ImportDecision> GetImportDecisions(List<String> videoFiles, Series series);
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource);
|
||||
}
|
||||
|
||||
public class ImportDecisionMaker : IMakeImportDecision
|
||||
{
|
||||
private readonly IEnumerable<IRejectWithReason> _specifications;
|
||||
private readonly IEnumerable<IImportDecisionEngineSpecification> _specifications;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly IDetectSample _detectSample;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportDecisionMaker(IEnumerable<IRejectWithReason> specifications,
|
||||
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
|
||||
IParsingService parsingService,
|
||||
IMediaFileService mediaFileService,
|
||||
IDiskProvider diskProvider,
|
||||
IVideoFileInfoReader videoFileInfoReader,
|
||||
IDetectSample detectSample,
|
||||
Logger logger)
|
||||
{
|
||||
_specifications = specifications;
|
||||
@ -39,98 +44,96 @@ public ImportDecisionMaker(IEnumerable<IRejectWithReason> specifications,
|
||||
_mediaFileService = mediaFileService;
|
||||
_diskProvider = diskProvider;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_detectSample = detectSample;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, bool sceneSource, QualityModel quality = null)
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series)
|
||||
{
|
||||
return GetImportDecisions(videoFiles, series, null, false);
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource)
|
||||
{
|
||||
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series);
|
||||
|
||||
_logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count());
|
||||
|
||||
return GetDecisions(newFiles, series, sceneSource, quality).ToList();
|
||||
var shouldUseFolderName = ShouldUseFolderName(videoFiles, series, folderInfo);
|
||||
var decisions = new List<ImportDecision>();
|
||||
|
||||
foreach (var file in newFiles)
|
||||
{
|
||||
decisions.AddIfNotNull(GetDecision(file, series, folderInfo, sceneSource, shouldUseFolderName));
|
||||
}
|
||||
|
||||
return decisions;
|
||||
}
|
||||
|
||||
private IEnumerable<ImportDecision> GetDecisions(IEnumerable<String> videoFiles, Series series, bool sceneSource, QualityModel quality = null)
|
||||
private ImportDecision GetDecision(string file, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource, bool shouldUseFolderName)
|
||||
{
|
||||
foreach (var file in videoFiles)
|
||||
ImportDecision decision = null;
|
||||
|
||||
try
|
||||
{
|
||||
ImportDecision decision = null;
|
||||
var localEpisode = _parsingService.GetLocalEpisode(file, series, shouldUseFolderName ? folderInfo : null, sceneSource);
|
||||
|
||||
try
|
||||
if (localEpisode != null)
|
||||
{
|
||||
var localEpisode = _parsingService.GetLocalEpisode(file, series, sceneSource);
|
||||
localEpisode.Quality = GetQuality(folderInfo, localEpisode.Quality, series);
|
||||
localEpisode.Size = _diskProvider.GetFileSize(file);
|
||||
|
||||
if (localEpisode != null)
|
||||
_logger.Debug("Size: {0}", localEpisode.Size);
|
||||
|
||||
//TODO: make it so media info doesn't ruin the import process of a new series
|
||||
if (sceneSource)
|
||||
{
|
||||
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);
|
||||
localEpisode.MediaInfo = _videoFileInfoReader.GetMediaInfo(file);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
localEpisode = new LocalEpisode();
|
||||
localEpisode.Path = file;
|
||||
|
||||
decision = new ImportDecision(localEpisode, "Unable to parse file");
|
||||
}
|
||||
decision = GetDecision(localEpisode);
|
||||
}
|
||||
catch (EpisodeNotFoundException e)
|
||||
|
||||
else
|
||||
{
|
||||
var localEpisode = new LocalEpisode();
|
||||
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;
|
||||
decision = new ImportDecision(localEpisode, new Rejection("Unable to parse file"));
|
||||
}
|
||||
}
|
||||
catch (EpisodeNotFoundException e)
|
||||
{
|
||||
var localEpisode = new LocalEpisode();
|
||||
localEpisode.Path = file;
|
||||
|
||||
decision = new ImportDecision(localEpisode, new Rejection(e.Message));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.ErrorException("Couldn't import file. " + file, e);
|
||||
}
|
||||
|
||||
return decision;
|
||||
}
|
||||
|
||||
private ImportDecision GetDecision(LocalEpisode localEpisode)
|
||||
{
|
||||
var reasons = _specifications.Select(c => EvaluateSpec(c, localEpisode))
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c));
|
||||
.Where(c => c != null);
|
||||
|
||||
return new ImportDecision(localEpisode, reasons.ToArray());
|
||||
}
|
||||
|
||||
private string EvaluateSpec(IRejectWithReason spec, LocalEpisode localEpisode)
|
||||
private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalEpisode localEpisode)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(spec.RejectionReason))
|
||||
{
|
||||
throw new InvalidOperationException("[Need Rejection Text]");
|
||||
}
|
||||
var result = spec.IsSatisfiedBy(localEpisode);
|
||||
|
||||
var generalSpecification = spec as IImportDecisionEngineSpecification;
|
||||
if (generalSpecification != null && !generalSpecification.IsSatisfiedBy(localEpisode))
|
||||
if (!result.Accepted)
|
||||
{
|
||||
return spec.RejectionReason;
|
||||
return new Rejection(result.Reason);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -138,10 +141,55 @@ private string EvaluateSpec(IRejectWithReason spec, LocalEpisode localEpisode)
|
||||
//e.Data.Add("report", remoteEpisode.Report.ToJson());
|
||||
//e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson());
|
||||
_logger.ErrorException("Couldn't evaluate decision on " + localEpisode.Path, e);
|
||||
return string.Format("{0}: {1}", spec.GetType().Name, e.Message);
|
||||
return new Rejection(String.Format("{0}: {1}", spec.GetType().Name, e.Message));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool ShouldUseFolderName(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo)
|
||||
{
|
||||
if (folderInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (folderInfo.FullSeason)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return videoFiles.Count(file =>
|
||||
{
|
||||
var size = _diskProvider.GetFileSize(file);
|
||||
var fileQuality = QualityParser.ParseQuality(file);
|
||||
var sample = _detectSample.IsSample(series, GetQuality(folderInfo, fileQuality, series), file, size, folderInfo.SeasonNumber);
|
||||
|
||||
if (sample)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SceneChecker.IsSceneTitle(Path.GetFileName(file)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}) == 1;
|
||||
}
|
||||
|
||||
private QualityModel GetQuality(ParsedEpisodeInfo folderInfo, QualityModel fileQuality, Series series)
|
||||
{
|
||||
if (folderInfo != null &&
|
||||
new QualityModelComparer(series.Profile).Compare(folderInfo.Quality,
|
||||
fileQuality) > 0)
|
||||
{
|
||||
_logger.Debug("Using quality from folder: {0}", folderInfo.Quality);
|
||||
return folderInfo.Quality;
|
||||
}
|
||||
|
||||
return fileQuality;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
|
||||
@ -8,7 +7,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
public class ImportResult
|
||||
{
|
||||
public ImportDecision ImportDecision { get; private set; }
|
||||
public List<String> Errors { get; private set; }
|
||||
public List<string> Errors { get; private set; }
|
||||
|
||||
public ImportResultType Result
|
||||
{
|
||||
@ -28,7 +27,7 @@ public ImportResultType Result
|
||||
}
|
||||
}
|
||||
|
||||
public ImportResult(ImportDecision importDecision, params String[] errors)
|
||||
public ImportResult(ImportDecision importDecision, params string[] errors)
|
||||
{
|
||||
Ensure.That(importDecision, () => importDecision).IsNotNull();
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
@ -20,14 +21,12 @@ public FreeSpaceSpecification(IDiskProvider diskProvider, IConfigService configS
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Not enough free space"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
public Decision IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (_configService.SkipFreeSpaceCheckWhenImporting)
|
||||
{
|
||||
_logger.Debug("Skipping free space check when importing");
|
||||
return true;
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
try
|
||||
@ -35,7 +34,7 @@ public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
if (localEpisode.ExistingFile)
|
||||
{
|
||||
_logger.Debug("Skipping free space check for existing episode");
|
||||
return true;
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var path = Directory.GetParent(localEpisode.Series.Path);
|
||||
@ -44,13 +43,13 @@ public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
if (!freeSpace.HasValue)
|
||||
{
|
||||
_logger.Debug("Free space check returned an invalid result for: {0}", path);
|
||||
return true;
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (freeSpace < localEpisode.Size + 100.Megabytes())
|
||||
{
|
||||
_logger.Warn("Not enough free space ({0}) to import: {1} ({2})", freeSpace, localEpisode, localEpisode.Size);
|
||||
return false;
|
||||
return Decision.Reject("Not enough free space");
|
||||
}
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
@ -62,7 +61,7 @@ public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
_logger.ErrorException("Unable to check free disk space while importing: " + localEpisode.Path, ex);
|
||||
}
|
||||
|
||||
return true;
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
@ -12,17 +13,15 @@ public FullSeasonSpecification(Logger logger)
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Full season file"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
public Decision IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (localEpisode.ParsedEpisodeInfo.FullSeason)
|
||||
{
|
||||
_logger.Debug("Single episode file detected as containing all episodes in the season");
|
||||
return false;
|
||||
return Decision.Reject("Single episode file contains all episodes in seasons");
|
||||
}
|
||||
|
||||
return true;
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
public class MatchesFolderSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MatchesFolderSpecification(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
public Decision IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (localEpisode.ExistingFile)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle(new FileInfo(localEpisode.Path).DirectoryName);
|
||||
|
||||
if (folderInfo == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (folderInfo.FullSeason)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var unexpected = localEpisode.ParsedEpisodeInfo.EpisodeNumbers.Where(f => !folderInfo.EpisodeNumbers.Contains(f)).ToList();
|
||||
|
||||
if (unexpected.Any())
|
||||
{
|
||||
_logger.Debug("Unexpected episode number(s) in file: {0}", unexpected);
|
||||
|
||||
if (unexpected.Count == 1)
|
||||
{
|
||||
return Decision.Reject("Episode Number {0} was unexpected", unexpected.First());
|
||||
}
|
||||
|
||||
return Decision.Reject("Episode Numbers {0} were unexpected", String.Join(", ", unexpected));
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +1,41 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
public class NotSampleSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly ISampleService _sampleService;
|
||||
private readonly IDetectSample _detectSample;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public NotSampleSpecification(ISampleService sampleService,
|
||||
public NotSampleSpecification(IDetectSample detectSample,
|
||||
Logger logger)
|
||||
{
|
||||
_sampleService = sampleService;
|
||||
_detectSample = detectSample;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Sample"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
public Decision IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (localEpisode.ExistingFile)
|
||||
{
|
||||
_logger.Debug("Existing file, skipping sample check");
|
||||
return true;
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
return !_sampleService.IsSample(localEpisode.Series,
|
||||
localEpisode.Quality,
|
||||
localEpisode.Path,
|
||||
localEpisode.Size,
|
||||
localEpisode.SeasonNumber);
|
||||
var sample = _detectSample.IsSample(localEpisode.Series,
|
||||
localEpisode.Quality,
|
||||
localEpisode.Path,
|
||||
localEpisode.Size,
|
||||
localEpisode.SeasonNumber);
|
||||
|
||||
if (sample)
|
||||
{
|
||||
return Decision.Reject("Sample");
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
@ -21,14 +22,12 @@ public NotUnpackingSpecification(IDiskProvider diskProvider, IConfigService conf
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "File is still being unpacked"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
public Decision IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (localEpisode.ExistingFile)
|
||||
{
|
||||
_logger.Debug("{0} is in series folder, unpacking check", localEpisode.Path);
|
||||
return true;
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
foreach (var workingFolder in _configService.DownloadClientWorkingFolders.Split('|'))
|
||||
@ -41,13 +40,13 @@ public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
if (OsInfo.IsNotWindows)
|
||||
{
|
||||
_logger.Debug("{0} is still being unpacked", localEpisode.Path);
|
||||
return false;
|
||||
return Decision.Reject("File is still being unpacked");
|
||||
}
|
||||
|
||||
if (_diskProvider.FileGetLastWrite(localEpisode.Path) > DateTime.UtcNow.AddMinutes(-5))
|
||||
{
|
||||
_logger.Debug("{0} appears to be unpacking still", localEpisode.Path);
|
||||
return false;
|
||||
return Decision.Reject("File is still being unpacked");
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +54,7 @@ public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
@ -14,18 +15,16 @@ public UpgradeSpecification(Logger logger)
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Not an upgrade for existing episode file(s)"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
public Decision IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
var qualityComparer = new QualityModelComparer(localEpisode.Series.Profile);
|
||||
if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && qualityComparer.Compare(e.EpisodeFile.Value.Quality, localEpisode.Quality) > 0))
|
||||
{
|
||||
_logger.Debug("This file isn't an upgrade for all episodes. Skipping {0}", localEpisode.Path);
|
||||
return false;
|
||||
return Decision.Reject("Not an upgrade for existing episode file(s)");
|
||||
}
|
||||
|
||||
return true;
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public void Handle(SeriesScannedEvent message)
|
||||
{
|
||||
try
|
||||
{
|
||||
var localEpisode = _parsingService.GetLocalEpisode(possibleMetadataFile, message.Series, false);
|
||||
var localEpisode = _parsingService.GetLocalEpisode(possibleMetadataFile, message.Series);
|
||||
|
||||
if (localEpisode == null)
|
||||
{
|
||||
|
@ -561,8 +561,9 @@
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMaker.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportResultType.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ManualImportService.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\SampleService.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\DetectSample.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecification.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\MatchesFolderSpecification.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecification.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecification.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecification.cs" />
|
||||
|
@ -104,7 +104,7 @@ public static class Parser
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//Episodes with single digit episode number (S01E1, S01E5E6, etc)
|
||||
new Regex(@"^(?<title>.*?)(?:\W?S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]){1,2}(?<episode>\d{1}))+)+(\W+|_|$)(?!\\)",
|
||||
new Regex(@"^(?<title>.*?)(?:(?:_|-|\s|\.)S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]){1,2}(?<episode>\d{1}))+)+(\W+|_|$)(?!\\)",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//Anime - Title Absolute Episode Number (e66)
|
||||
|
@ -13,7 +13,8 @@ namespace NzbDrone.Core.Parser
|
||||
{
|
||||
public interface IParsingService
|
||||
{
|
||||
LocalEpisode GetLocalEpisode(string filename, Series series, bool sceneSource);
|
||||
LocalEpisode GetLocalEpisode(string filename, Series series);
|
||||
LocalEpisode GetLocalEpisode(string filename, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource);
|
||||
Series GetSeries(string title);
|
||||
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 tvRageId = 0, SearchCriteriaBase searchCriteria = null);
|
||||
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 seriesId, IEnumerable<Int32> episodeIds);
|
||||
@ -39,9 +40,25 @@ public ParsingService(IEpisodeService episodeService,
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public LocalEpisode GetLocalEpisode(string filename, Series series, bool sceneSource)
|
||||
public LocalEpisode GetLocalEpisode(string filename, Series series)
|
||||
{
|
||||
var parsedEpisodeInfo = Parser.ParsePath(filename);
|
||||
return GetLocalEpisode(filename, series, null, false);
|
||||
}
|
||||
|
||||
public LocalEpisode GetLocalEpisode(string filename, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource)
|
||||
{
|
||||
ParsedEpisodeInfo parsedEpisodeInfo;
|
||||
|
||||
if (folderInfo != null)
|
||||
{
|
||||
parsedEpisodeInfo = folderInfo.JsonClone();
|
||||
parsedEpisodeInfo.Quality = QualityParser.ParseQuality(Path.GetFileName(filename));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
parsedEpisodeInfo = Parser.ParsePath(filename);
|
||||
}
|
||||
|
||||
if (parsedEpisodeInfo == null || parsedEpisodeInfo.IsPossibleSpecialEpisode)
|
||||
{
|
||||
|
@ -1,4 +1,6 @@
|
||||
namespace NzbDrone.Core.Parser
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.Parser
|
||||
{
|
||||
public static class SceneChecker
|
||||
{
|
||||
@ -10,10 +12,14 @@ public static bool IsSceneTitle(string title)
|
||||
if (title.Contains(" ")) return false;
|
||||
|
||||
var parsedTitle = Parser.ParseTitle(title);
|
||||
if (parsedTitle == null
|
||||
|| parsedTitle.ReleaseGroup == null
|
||||
|| parsedTitle.Quality.Quality == Qualities.Quality.Unknown
|
||||
|| string.IsNullOrWhiteSpace(parsedTitle.SeriesTitle)) return false;
|
||||
|
||||
if (parsedTitle == null ||
|
||||
parsedTitle.ReleaseGroup == null ||
|
||||
parsedTitle.Quality.Quality == Qualities.Quality.Unknown ||
|
||||
String.IsNullOrWhiteSpace(parsedTitle.SeriesTitle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -91,7 +91,6 @@
|
||||
<Compile Include="LoggingTest.cs" />
|
||||
<Compile Include="MockerExtensions.cs" />
|
||||
<Compile Include="NzbDroneRunner.cs" />
|
||||
<Compile Include="ObjectExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ReflectionExtensions.cs" />
|
||||
<Compile Include="StringExtensions.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user