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