mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-16 11:37:58 +02:00
Episode import uses specs and moves before import now
This commit is contained in:
parent
9ed5a06504
commit
aeb8ee06f6
@ -199,7 +199,7 @@ public void broken_report_shouldnt_blowup_the_process()
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_return_unknown_series_rejection_if_series_is_unknow()
|
||||
public void should_return_unknown_series_rejection_if_series_is_unknown()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
|
||||
|
@ -55,7 +55,7 @@ public void should_not_move_file_if_source_and_destination_are_the_same_path()
|
||||
.Setup(e => e.BuildFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".avi"))
|
||||
.Returns(fi);
|
||||
|
||||
var result = Subject.MoveEpisodeFile(file, false);
|
||||
var result = Subject.MoveEpisodeFile(file);
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
@ -106,7 +106,7 @@ public void should_use_EpisodeFiles_quality()
|
||||
.Setup(s => s.FileExists(currentFilename))
|
||||
.Returns(true);
|
||||
|
||||
var result = Subject.MoveEpisodeFile(file, true);
|
||||
var result = Subject.MoveEpisodeFile(file);
|
||||
|
||||
|
||||
}
|
||||
@ -153,7 +153,7 @@ public void should_log_error_and_return_null_when_source_file_does_not_exists()
|
||||
.Setup(e => e.BuildFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".mkv"))
|
||||
.Returns(fi);
|
||||
|
||||
var result = Subject.MoveEpisodeFile(file, true);
|
||||
var result = Subject.MoveEpisodeFile(file);
|
||||
|
||||
result.Should().BeNull();
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
|
@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ImportDecisionMakerFixture : CoreTest<ImportDecisionMaker>
|
||||
{
|
||||
private List<String> _videoFiles;
|
||||
private LocalEpisode _localEpisode;
|
||||
private Series _series;
|
||||
|
||||
private Mock<IImportDecisionEngineSpecification> _pass1;
|
||||
private Mock<IImportDecisionEngineSpecification> _pass2;
|
||||
private Mock<IImportDecisionEngineSpecification> _pass3;
|
||||
|
||||
private Mock<IImportDecisionEngineSpecification> _fail1;
|
||||
private Mock<IImportDecisionEngineSpecification> _fail2;
|
||||
private Mock<IImportDecisionEngineSpecification> _fail3;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_pass1 = new Mock<IImportDecisionEngineSpecification>();
|
||||
_pass2 = new Mock<IImportDecisionEngineSpecification>();
|
||||
_pass3 = new Mock<IImportDecisionEngineSpecification>();
|
||||
|
||||
_fail1 = new Mock<IImportDecisionEngineSpecification>();
|
||||
_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");
|
||||
|
||||
_pass2.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(true);
|
||||
_pass2.Setup(c => c.RejectionReason).Returns("_pass2");
|
||||
|
||||
_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> { "The.Office.S03E115.DVDRip.XviD-OSiTV" };
|
||||
_series = new Series();
|
||||
_localEpisode = new LocalEpisode { Series = _series, Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" };
|
||||
|
||||
Mocker.GetMock<IParsingService>().Setup(c => c.GetEpisodes(It.IsAny<String>(), It.IsAny<Series>()))
|
||||
.Returns(_localEpisode);
|
||||
|
||||
}
|
||||
|
||||
private void GivenSpecifications(params Mock<IImportDecisionEngineSpecification>[] mocks)
|
||||
{
|
||||
Mocker.SetConstant<IEnumerable<IRejectWithReason>>(mocks.Select(c => c.Object));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_call_all_specifications()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
|
||||
_fail1.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
|
||||
_fail2.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
|
||||
_fail3.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
|
||||
_pass1.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
|
||||
_pass2.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
|
||||
_pass3.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_return_rejected_if_single_specs_fail()
|
||||
{
|
||||
GivenSpecifications(_fail1);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
|
||||
result.Single().Approved.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_rejected_if_one_of_specs_fail()
|
||||
{
|
||||
GivenSpecifications(_pass1, _fail1, _pass2, _pass3);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
|
||||
result.Single().Approved.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_pass_if_all_specs_pass()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
|
||||
result.Single().Approved.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
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());
|
||||
result.Single().Rejections.Should().HaveCount(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void failed_parse_shouldnt_blowup_the_process()
|
||||
{
|
||||
GivenSpecifications(_pass1);
|
||||
|
||||
Mocker.GetMock<IParsingService>().Setup(c => c.GetEpisodes(It.IsAny<String>(), It.IsAny<Series>()))
|
||||
.Throws<TestException>();
|
||||
|
||||
_videoFiles = new List<String>
|
||||
{
|
||||
"The.Office.S03E115.DVDRip.XviD-OSiTV",
|
||||
"The.Office.S03E115.DVDRip.XviD-OSiTV",
|
||||
"The.Office.S03E115.DVDRip.XviD-OSiTV"
|
||||
};
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, new Series());
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetEpisodes(It.IsAny<String>(), It.IsAny<Series>()), Times.Exactly(_videoFiles.Count));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(3);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class NotAlreadyImportedSpecificationFixture : CoreTest<NotAlreadyImportedSpecification>
|
||||
{
|
||||
private LocalEpisode _localEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_localEpisode = new LocalEpisode
|
||||
{
|
||||
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi"
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_path_is_already_in_episodeFiles()
|
||||
{
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(s => s.Exists(_localEpisode.Path))
|
||||
.Returns(true);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_new_file()
|
||||
{
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(s => s.Exists(_localEpisode.Path))
|
||||
.Returns(false);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class NotSampleSpecificationFixture : CoreTest<NotSampleSpecification>
|
||||
{
|
||||
private Series _series;
|
||||
private LocalEpisode _localEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(s => s.SeriesType = SeriesTypes.Standard)
|
||||
.Build();
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
_localEpisode = new LocalEpisode
|
||||
{
|
||||
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi",
|
||||
Episodes = episodes,
|
||||
Series = _series
|
||||
};
|
||||
}
|
||||
|
||||
private void WithDailySeries()
|
||||
{
|
||||
_series.SeriesType = SeriesTypes.Daily;
|
||||
}
|
||||
|
||||
private void WithSeasonZero()
|
||||
{
|
||||
_localEpisode.Episodes[0].SeasonNumber = 0;
|
||||
}
|
||||
|
||||
private void WithFileSize(long size)
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.GetFileSize(It.IsAny<String>()))
|
||||
.Returns(size);
|
||||
}
|
||||
|
||||
private void WithLength(int minutes)
|
||||
{
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Setup(s => s.GetRunTime(It.IsAny<String>()))
|
||||
.Returns(new TimeSpan(0, 0, minutes, 0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_series_is_daily()
|
||||
{
|
||||
WithDailySeries();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_season_zero()
|
||||
{
|
||||
WithSeasonZero();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_undersize_and_under_length()
|
||||
{
|
||||
WithFileSize(10.Megabytes());
|
||||
WithLength(1);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_undersize()
|
||||
{
|
||||
WithFileSize(10.Megabytes());
|
||||
WithLength(10);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_under_length()
|
||||
{
|
||||
WithFileSize(100.Megabytes());
|
||||
WithLength(1);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_over_size_and_length()
|
||||
{
|
||||
WithFileSize(100.Megabytes());
|
||||
WithLength(10);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class UpgradeSpecificationFixture : CoreTest<UpgradeSpecification>
|
||||
{
|
||||
private Series _series;
|
||||
private LocalEpisode _localEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(s => s.SeriesType = SeriesTypes.Standard)
|
||||
.Build();
|
||||
|
||||
_localEpisode = new LocalEpisode
|
||||
{
|
||||
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi",
|
||||
Quality = new QualityModel(Quality.HDTV720p, false)
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_no_existing_episodeFile()
|
||||
{
|
||||
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.EpisodeFileId = 0)
|
||||
.With(e => e.EpisodeFile = null)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_no_existing_episodeFile_for_multi_episodes()
|
||||
{
|
||||
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(e => e.EpisodeFileId = 0)
|
||||
.With(e => e.EpisodeFile = null)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_upgrade_for_existing_episodeFile()
|
||||
{
|
||||
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.EpisodeFileId = 1)
|
||||
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
|
||||
new EpisodeFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.SDTV, false)
|
||||
}))
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_upgrade_for_existing_episodeFile_for_multi_episodes()
|
||||
{
|
||||
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(e => e.EpisodeFileId = 1)
|
||||
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
|
||||
new EpisodeFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.SDTV, false)
|
||||
}))
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_not_an_upgrade_for_existing_episodeFile()
|
||||
{
|
||||
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.EpisodeFileId = 1)
|
||||
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
|
||||
new EpisodeFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.Bluray720p, false)
|
||||
}))
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_not_an_upgrade_for_existing_episodeFile_for_multi_episodes()
|
||||
{
|
||||
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(e => e.EpisodeFileId = 1)
|
||||
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
|
||||
new EpisodeFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.Bluray720p, false)
|
||||
}))
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_not_an_upgrade_for_one_existing_episodeFile_for_multi_episode()
|
||||
{
|
||||
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.TheFirst(1)
|
||||
.With(e => e.EpisodeFileId = 1)
|
||||
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
|
||||
new EpisodeFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.SDTV, false)
|
||||
}))
|
||||
.TheNext(1)
|
||||
.With(e => e.EpisodeFileId = 2)
|
||||
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
|
||||
new EpisodeFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.Bluray720p, false)
|
||||
}))
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@ -148,6 +148,10 @@
|
||||
<Compile Include="JobTests\TestJobs.cs" />
|
||||
<Compile Include="MediaCoverTests\CoverExistsSpecificationFixture.cs" />
|
||||
<Compile Include="MediaCoverTests\MediaCoverServiceFixture.cs" />
|
||||
<Compile Include="MediaFileTests\EpisodeImportTests\UpgradeSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFileTests\EpisodeImportTests\NotSampleSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFileTests\EpisodeImportTests\ImportDecisionMakerFixture.cs" />
|
||||
<Compile Include="MediaFileTests\EpisodeImportTests\NotAlreadyImportedSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFileTests\MediaFileTableCleanupServiceFixture.cs" />
|
||||
<Compile Include="MediaFileTests\MediaFileRepositoryFixture.cs" />
|
||||
<Compile Include="MediaFileTests\EpisodeFileMoverFixture.cs" />
|
||||
@ -200,7 +204,6 @@
|
||||
<Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" />
|
||||
<Compile Include="Qualities\QualitySizeServiceFixture.cs" />
|
||||
<Compile Include="TvTests\EpisodeProviderTests\EpisodeProviderTest_GetEpisodesByParseResult.cs" />
|
||||
<Compile Include="ProviderTests\DiskScanProviderTests\ImportFileFixture.cs" />
|
||||
<Compile Include="FluentTest.cs" />
|
||||
<Compile Include="InstrumentationTests\DatabaseTargetFixture.cs" />
|
||||
<Compile Include="OrganizerTests\GetNewFilenameFixture.cs" />
|
||||
|
@ -1,303 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
|
||||
{
|
||||
|
||||
public class ImportFileFixture : CoreTest<DiskScanService>
|
||||
{
|
||||
|
||||
|
||||
private long _fileSize = 80.Megabytes();
|
||||
private Series _fakeSeries;
|
||||
private Episode[] _fakeEpisodes;
|
||||
private Episode _fakeEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_fakeSeries = Builder<Series>
|
||||
.CreateNew()
|
||||
.Build();
|
||||
|
||||
_fakeEpisode = Builder<Episode>
|
||||
.CreateNew()
|
||||
.With(c => c.EpisodeFileId = 0)
|
||||
.Build();
|
||||
|
||||
|
||||
_fakeEpisodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(c => c.SeasonNumber = 3)
|
||||
.With(c => c.EpisodeFileId = 1)
|
||||
.With(e => e.EpisodeFile = new EpisodeFile())
|
||||
.BuildList().ToArray();
|
||||
|
||||
GivenNewFile();
|
||||
|
||||
GivenVideoDuration(TimeSpan.FromMinutes(20));
|
||||
|
||||
GivenFileSize(_fileSize);
|
||||
|
||||
}
|
||||
|
||||
private void GivenFileSize(long size)
|
||||
{
|
||||
_fileSize = size;
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(d => d.GetFileSize(It.IsAny<String>()))
|
||||
.Returns(size);
|
||||
}
|
||||
|
||||
private void GivenVideoDuration(TimeSpan duration)
|
||||
{
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Setup(d => d.GetRunTime(It.IsAny<String>()))
|
||||
.Returns(duration);
|
||||
}
|
||||
|
||||
|
||||
private void GivenEpisodes(Episode[] episodes, QualityModel quality)
|
||||
{
|
||||
foreach (var episode in episodes)
|
||||
{
|
||||
if (episode.EpisodeFile == null)
|
||||
{
|
||||
episode.EpisodeFileId = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
episode.EpisodeFileId = episode.EpisodeFile.Value.Id;
|
||||
}
|
||||
}
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetEpisodes(It.IsAny<string>(), It.IsAny<Series>()))
|
||||
.Returns(new LocalEpisode
|
||||
{
|
||||
Episodes = episodes.ToList(),
|
||||
Quality = quality
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenNewFile()
|
||||
{
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(p => p.Exists(It.IsAny<String>()))
|
||||
.Returns(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void import_new_file_should_succeed()
|
||||
{
|
||||
GivenEpisodes(new[] { _fakeEpisode }, new QualityModel());
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
VerifyFileImport(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Test]
|
||||
public void import_new_file_with_same_quality_should_succeed()
|
||||
{
|
||||
_fakeEpisode.EpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.SDTV) };
|
||||
|
||||
GivenEpisodes(new[] { _fakeEpisode }, new QualityModel(Quality.SDTV));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
VerifyFileImport(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void import_new_file_with_better_quality_should_succeed()
|
||||
{
|
||||
_fakeEpisode.EpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.SDTV) };
|
||||
|
||||
GivenEpisodes(new[] { _fakeEpisode }, new QualityModel(Quality.HDTV1080p));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
VerifyFileImport(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void import_new_file_episode_has_better_quality_should_skip()
|
||||
{
|
||||
_fakeEpisode.EpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV1080p), Id = 1 };
|
||||
|
||||
GivenEpisodes(new[] { _fakeEpisode }, new QualityModel(Quality.SDTV));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
VerifySkipImport(result);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void import_unparsable_file_should_skip()
|
||||
{
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetEpisodes(It.IsAny<string>(), It.IsAny<Series>()))
|
||||
.Returns<LocalEpisode>(null);
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
|
||||
VerifySkipImport(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void import_existing_file_should_skip()
|
||||
{
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(p => p.Exists(It.IsAny<String>()))
|
||||
.Returns(true);
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
VerifySkipImport(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void import_file_with_no_episode_in_db_should_skip()
|
||||
{
|
||||
GivenEpisodes(new Episode[0], new QualityModel());
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
VerifySkipImport(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void import_new_multi_part_file_episode_with_better_quality_than_existing()
|
||||
{
|
||||
_fakeEpisodes[0].EpisodeFile = new EpisodeFile();
|
||||
_fakeEpisodes[1].EpisodeFile = new EpisodeFile();
|
||||
|
||||
_fakeEpisodes[0].EpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.SDTV) };
|
||||
_fakeEpisodes[1].EpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.SDTV) };
|
||||
|
||||
GivenEpisodes(_fakeEpisodes, new QualityModel(Quality.HDTV1080p));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
|
||||
VerifyFileImport(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void skip_import_new_multi_part_file_episode_existing_has_better_quality()
|
||||
{
|
||||
_fakeEpisodes[0].EpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV1080p), Id = 1 };
|
||||
_fakeEpisodes[1].EpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV1080p), Id = 1 };
|
||||
|
||||
GivenEpisodes(_fakeEpisodes, new QualityModel(Quality.SDTV));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
|
||||
VerifySkipImport(result);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_skip_if_file_size_is_under_70MB_and_runTime_under_3_minutes()
|
||||
{
|
||||
GivenFileSize(50.Megabytes());
|
||||
GivenVideoDuration(TimeSpan.FromMinutes(1));
|
||||
|
||||
GivenEpisodes(new[] { _fakeEpisode }, new QualityModel(Quality.HDTV1080p));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
VerifySkipImport(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_import_if_file_size_is_under_70MB_but_runTime_over_3_minutes()
|
||||
{
|
||||
GivenFileSize(50.Megabytes());
|
||||
GivenVideoDuration(TimeSpan.FromMinutes(20));
|
||||
|
||||
GivenEpisodes(new[] { _fakeEpisode }, new QualityModel(Quality.HDTV1080p));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
VerifyFileImport(result);
|
||||
Mocker.GetMock<IDiskProvider>().Verify(p => p.DeleteFile(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_import_if_file_size_is_over_70MB_but_runTime_under_3_minutes()
|
||||
{
|
||||
GivenFileSize(100.Megabytes());
|
||||
GivenVideoDuration(TimeSpan.FromMinutes(1));
|
||||
|
||||
GivenEpisodes(new[] { _fakeEpisode }, new QualityModel(Quality.HDTV1080p));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
VerifyFileImport(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_import_special_even_if_file_size_is_under_70MB_and_runTime_under_3_minutes()
|
||||
{
|
||||
GivenFileSize(10.Megabytes());
|
||||
GivenVideoDuration(TimeSpan.FromMinutes(1));
|
||||
|
||||
_fakeEpisode.SeasonNumber = 0;
|
||||
|
||||
GivenEpisodes(new[] { _fakeEpisode }, new QualityModel(Quality.HDTV1080p));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
VerifyFileImport(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_if_daily_series_with_file_size_is_under_70MB_and_runTime_under_3_minutes()
|
||||
{
|
||||
GivenFileSize(10.Megabytes());
|
||||
GivenVideoDuration(TimeSpan.FromMinutes(1));
|
||||
|
||||
_fakeEpisode.SeasonNumber = 0;
|
||||
_fakeSeries.SeriesType = SeriesTypes.Daily;
|
||||
|
||||
GivenEpisodes(new[] { _fakeEpisode }, new QualityModel(Quality.HDTV1080p));
|
||||
|
||||
var result = Subject.ImportFile(_fakeSeries, "file.ext");
|
||||
|
||||
VerifySkipImport(result);
|
||||
}
|
||||
|
||||
private void VerifyFileImport(EpisodeFile result)
|
||||
{
|
||||
result.Should().NotBeNull();
|
||||
result.SeriesId.Should().Be(_fakeSeries.Id);
|
||||
result.Size.Should().Be(_fileSize);
|
||||
result.DateAdded.Should().HaveDay(DateTime.UtcNow.Day);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(c => c.Add(result), Times.Once());
|
||||
}
|
||||
|
||||
private void VerifySkipImport(EpisodeFile result)
|
||||
{
|
||||
result.Should().BeNull();
|
||||
Mocker.GetMock<IMediaFileService>().Verify(p => p.Add(It.IsAny<EpisodeFile>()), Times.Never());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
@ -6,6 +7,7 @@
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -26,7 +28,6 @@ public void Setup()
|
||||
{
|
||||
_fakeEpisodeFile = Builder<EpisodeFile>.CreateNew().Build();
|
||||
|
||||
|
||||
Mocker.GetMock<IDiskScanService>().Setup(c => c.GetVideoFiles(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(_videoFiles);
|
||||
|
||||
@ -37,24 +38,6 @@ public void Setup()
|
||||
.Returns("c:\\drop\\");
|
||||
}
|
||||
|
||||
private void WithOldWrite()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetLastFolderWrite(It.IsAny<String>()))
|
||||
.Returns(DateTime.Now.AddDays(-5));
|
||||
}
|
||||
|
||||
private void WithRecentFolderWrite()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetLastFolderWrite(It.IsAny<String>()))
|
||||
.Returns(DateTime.UtcNow);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetLastFileWrite(It.IsAny<String>()))
|
||||
.Returns(DateTime.UtcNow);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_import_file()
|
||||
{
|
||||
@ -66,65 +49,14 @@ public void should_import_file()
|
||||
[Test]
|
||||
public void should_search_for_series_using_folder_name()
|
||||
{
|
||||
WithOldWrite();
|
||||
|
||||
Subject.ProcessDownloadedEpisodesFolder();
|
||||
|
||||
Mocker.GetMock<IParsingService>().Verify(c => c.GetSeries("foldername"), Times.Once());
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void all_imported_files_should_be_moved()
|
||||
public void should_skip_if_file_is_in_use_by_another_process()
|
||||
{
|
||||
Mocker.GetMock<IDiskScanService>().Setup(c => c.ImportFile(It.IsAny<Series>(), It.IsAny<string>()))
|
||||
.Returns(_fakeEpisodeFile);
|
||||
|
||||
Subject.ProcessDownloadedEpisodesFolder();
|
||||
|
||||
Mocker.GetMock<IMoveEpisodeFiles>().Verify(c => c.MoveEpisodeFile(_fakeEpisodeFile, true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_trigger_import_event_on_import()
|
||||
{
|
||||
Mocker.GetMock<IDiskScanService>().Setup(c => c.ImportFile(It.IsAny<Series>(), It.IsAny<string>()))
|
||||
.Returns(_fakeEpisodeFile);
|
||||
|
||||
Subject.ProcessDownloadedEpisodesFolder();
|
||||
|
||||
VerifyEventPublished<EpisodeImportedEvent>();
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_attempt_move_if_nothing_is_imported()
|
||||
{
|
||||
Mocker.GetMock<IDiskScanService>().Setup(c => c.ImportFile(It.IsAny<Series>(), It.IsAny<string>()))
|
||||
.Returns<EpisodeFile>(null);
|
||||
|
||||
Subject.ProcessDownloadedEpisodesFolder();
|
||||
|
||||
Mocker.GetMock<IMoveEpisodeFiles>().Verify(c => c.MoveEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<bool>()), Times.Never());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_not_publish_import_event_if_nothing_is_imported()
|
||||
{
|
||||
Mocker.GetMock<IDiskScanService>().Setup(c => c.ImportFile(It.IsAny<Series>(), It.IsAny<string>()))
|
||||
.Returns<EpisodeFile>(null);
|
||||
|
||||
Subject.ProcessDownloadedEpisodesFolder();
|
||||
|
||||
|
||||
VerifyEventNotPublished<EpisodeImportedEvent>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_if_folder_is_in_use_by_another_process()
|
||||
{
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.IsFileLocked(It.IsAny<FileInfo>()))
|
||||
.Returns(true);
|
||||
|
||||
@ -134,13 +66,13 @@ public void should_skip_if_folder_is_in_use_by_another_process()
|
||||
|
||||
private void VerifyNoImport()
|
||||
{
|
||||
Mocker.GetMock<IDiskScanService>().Verify(c => c.ImportFile(It.IsAny<Series>(), It.IsAny<string>()),
|
||||
Mocker.GetMock<IImportApprovedEpisodes>().Verify(c => c.Import(It.IsAny<List<ImportDecision>>(), true),
|
||||
Times.Never());
|
||||
}
|
||||
|
||||
private void VerifyImport()
|
||||
{
|
||||
Mocker.GetMock<IDiskScanService>().Verify(c => c.ImportFile(It.IsAny<Series>(), It.IsAny<string>()),
|
||||
Mocker.GetMock<IImportApprovedEpisodes>().Verify(c => c.Import(It.IsAny<List<ImportDecision>>(), true),
|
||||
Times.Once());
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -15,7 +16,6 @@ namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public interface IDiskScanService
|
||||
{
|
||||
EpisodeFile ImportFile(Series series, string filePath);
|
||||
string[] GetVideoFiles(string path, bool allDirectories = true);
|
||||
}
|
||||
|
||||
@ -25,19 +25,20 @@ public class DiskScanService : IDiskScanService, IExecute<DiskScanCommand>, IHan
|
||||
private static readonly string[] MediaExtensions = new[] { ".mkv", ".avi", ".wmv", ".mp4", ".mpg", ".mpeg", ".xvid", ".flv", ".mov", ".rm", ".rmvb", ".divx", ".dvr-ms", ".ts", ".ogm", ".m4v", ".strm" };
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IMakeImportDecision _importDecisionMaker;
|
||||
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
||||
private readonly IMessageAggregator _messageAggregator;
|
||||
|
||||
public DiskScanService(IDiskProvider diskProvider, ISeriesService seriesService, IMediaFileService mediaFileService, IVideoFileInfoReader videoFileInfoReader,
|
||||
IParsingService parsingService, IMessageAggregator messageAggregator)
|
||||
public DiskScanService(IDiskProvider diskProvider,
|
||||
ISeriesService seriesService,
|
||||
IMakeImportDecision importDecisionMaker,
|
||||
IImportApprovedEpisodes importApprovedEpisodes,
|
||||
IMessageAggregator messageAggregator)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_seriesService = seriesService;
|
||||
_mediaFileService = mediaFileService;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_parsingService = parsingService;
|
||||
_importDecisionMaker = importDecisionMaker;
|
||||
_importApprovedEpisodes = importApprovedEpisodes;
|
||||
_messageAggregator = messageAggregator;
|
||||
}
|
||||
|
||||
@ -53,71 +54,8 @@ private void Scan(Series series)
|
||||
|
||||
var mediaFileList = GetVideoFiles(series.Path);
|
||||
|
||||
foreach (var filePath in mediaFileList)
|
||||
{
|
||||
try
|
||||
{
|
||||
ImportFile(series, filePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException("Couldn't import file " + filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
//Todo: Find the "best" episode file for all found episodes and import that one
|
||||
//Todo: Move the episode linking to here, instead of import (or rename import)
|
||||
}
|
||||
|
||||
public EpisodeFile ImportFile(Series series, string filePath)
|
||||
{
|
||||
Logger.Trace("Importing file to database [{0}]", filePath);
|
||||
|
||||
if (_mediaFileService.Exists(filePath))
|
||||
{
|
||||
Logger.Trace("[{0}] already exists in the database. skipping.", filePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
var parsedEpisode = _parsingService.GetEpisodes(filePath, series);
|
||||
|
||||
if (parsedEpisode == null || !parsedEpisode.Episodes.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var size = _diskProvider.GetFileSize(filePath);
|
||||
|
||||
if (series.SeriesType == SeriesTypes.Daily || parsedEpisode.SeasonNumber > 0)
|
||||
{
|
||||
var runTime = _videoFileInfoReader.GetRunTime(filePath);
|
||||
if (size < Constants.IgnoreFileSize && runTime.TotalMinutes < 3)
|
||||
{
|
||||
Logger.Trace("[{0}] appears to be a sample. skipping.", filePath);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && e.EpisodeFile.Value.Quality > parsedEpisode.Quality))
|
||||
{
|
||||
Logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", filePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
var episodeFile = new EpisodeFile();
|
||||
episodeFile.DateAdded = DateTime.UtcNow;
|
||||
episodeFile.SeriesId = series.Id;
|
||||
episodeFile.Path = filePath.CleanPath();
|
||||
episodeFile.Size = size;
|
||||
episodeFile.Quality = parsedEpisode.Quality;
|
||||
episodeFile.SeasonNumber = parsedEpisode.SeasonNumber;
|
||||
episodeFile.SceneName = Path.GetFileNameWithoutExtension(filePath.CleanPath());
|
||||
episodeFile.Episodes = parsedEpisode.Episodes;
|
||||
|
||||
//Todo: We shouldn't actually import the file until we confirm its the only one we want.
|
||||
//Todo: Separate episodeFile creation from importing (pass file to import to import)
|
||||
_mediaFileService.Add(episodeFile);
|
||||
return episodeFile;
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, series);
|
||||
_importApprovedEpisodes.Import(decisions);
|
||||
}
|
||||
|
||||
public string[] GetVideoFiles(string path, bool allDirectories = true)
|
||||
|
@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
@ -19,7 +20,8 @@ public class DownloadedEpisodesImportService : IExecute<DownloadedEpisodesScanCo
|
||||
private readonly IMoveEpisodeFiles _episodeFileMover;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IMessageAggregator _messageAggregator;
|
||||
private readonly IMakeImportDecision _importDecisionMaker;
|
||||
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
@ -28,7 +30,8 @@ public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
IMoveEpisodeFiles episodeFileMover,
|
||||
IParsingService parsingService,
|
||||
IConfigService configService,
|
||||
IMessageAggregator messageAggregator,
|
||||
IMakeImportDecision importDecisionMaker,
|
||||
IImportApprovedEpisodes importApprovedEpisodes,
|
||||
Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
@ -37,7 +40,8 @@ public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
_episodeFileMover = episodeFileMover;
|
||||
_parsingService = parsingService;
|
||||
_configService = configService;
|
||||
_messageAggregator = messageAggregator;
|
||||
_importDecisionMaker = importDecisionMaker;
|
||||
_importApprovedEpisodes = importApprovedEpisodes;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -92,7 +96,7 @@ public void ProcessDownloadedEpisodesFolder()
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessSubFolder(DirectoryInfo subfolderInfo)
|
||||
private void ProcessSubFolder(DirectoryInfo subfolderInfo)
|
||||
{
|
||||
var series = _parsingService.GetSeries(subfolderInfo.Name);
|
||||
|
||||
@ -102,12 +106,9 @@ public void ProcessSubFolder(DirectoryInfo subfolderInfo)
|
||||
return;
|
||||
}
|
||||
|
||||
var files = _diskScanService.GetVideoFiles(subfolderInfo.FullName);
|
||||
var videoFiles = _diskScanService.GetVideoFiles(subfolderInfo.FullName);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
ProcessVideoFile(file, series);
|
||||
}
|
||||
ProcessFiles(videoFiles, series);
|
||||
}
|
||||
|
||||
private void ProcessVideoFile(string videoFile, Series series)
|
||||
@ -118,13 +119,13 @@ private void ProcessVideoFile(string videoFile, Series series)
|
||||
return;
|
||||
}
|
||||
|
||||
var episodeFile = _diskScanService.ImportFile(series, videoFile);
|
||||
|
||||
if (episodeFile != null)
|
||||
{
|
||||
_episodeFileMover.MoveEpisodeFile(episodeFile, true);
|
||||
_messageAggregator.PublishEvent(new EpisodeImportedEvent(episodeFile));
|
||||
ProcessFiles(new [] { videoFile }, series);
|
||||
}
|
||||
|
||||
private void ProcessFiles(IEnumerable<string> videoFiles, Series series)
|
||||
{
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles, series);
|
||||
_importApprovedEpisodes.Import(decisions, true);
|
||||
}
|
||||
|
||||
public void Execute(DownloadedEpisodesScanCommand message)
|
||||
|
@ -6,13 +6,15 @@
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public interface IMoveEpisodeFiles
|
||||
{
|
||||
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false);
|
||||
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile);
|
||||
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
|
||||
}
|
||||
|
||||
public class MoveEpisodeFiles : IMoveEpisodeFiles
|
||||
@ -36,55 +38,68 @@ public MoveEpisodeFiles(ISeriesRepository seriesRepository, IEpisodeService epis
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false)
|
||||
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile)
|
||||
{
|
||||
if (episodeFile == null)
|
||||
throw new ArgumentNullException("episodeFile");
|
||||
|
||||
var series = _seriesRepository.Get(episodeFile.SeriesId);
|
||||
var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id);
|
||||
string newFileName = _buildFileNames.BuildFilename(episodes, series, episodeFile);
|
||||
var newFile = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
|
||||
var newFileName = _buildFileNames.BuildFilename(episodes, series, episodeFile);
|
||||
var destinationFilename = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
|
||||
|
||||
//Only rename if existing and new filenames don't match
|
||||
if (DiskProvider.PathEquals(episodeFile.Path, newFile))
|
||||
{
|
||||
_logger.Debug("Skipping file rename, source and destination are the same: {0}", episodeFile.Path);
|
||||
return null;
|
||||
episodeFile = MoveFile(episodeFile, destinationFilename);
|
||||
|
||||
_mediaFileService.Update(episodeFile);
|
||||
|
||||
return episodeFile;
|
||||
}
|
||||
|
||||
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
|
||||
{
|
||||
var newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile);
|
||||
var destinationFilename = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
|
||||
episodeFile = MoveFile(episodeFile, destinationFilename);
|
||||
|
||||
//TODO: This just re-parses the source path (which is how we got localEpisode to begin with)
|
||||
var parsedEpisodeInfo = Parser.Parser.ParsePath(localEpisode.Path);
|
||||
_messageAggregator.PublishEvent(new EpisodeDownloadedEvent(parsedEpisodeInfo, localEpisode.Series));
|
||||
|
||||
return episodeFile;
|
||||
}
|
||||
|
||||
private EpisodeFile MoveFile(EpisodeFile episodeFile, string destinationFilename)
|
||||
{
|
||||
if (!_diskProvider.FileExists(episodeFile.Path))
|
||||
{
|
||||
_logger.Error("Episode file path does not exist, {0}", episodeFile.Path);
|
||||
return null;
|
||||
}
|
||||
|
||||
_diskProvider.CreateFolder(new FileInfo(newFile).DirectoryName);
|
||||
//Only rename if existing and new filenames don't match
|
||||
if (DiskProvider.PathEquals(episodeFile.Path, destinationFilename))
|
||||
{
|
||||
_logger.Debug("Skipping file rename, source and destination are the same: {0}", episodeFile.Path);
|
||||
return null;
|
||||
}
|
||||
|
||||
_logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, newFile);
|
||||
_diskProvider.MoveFile(episodeFile.Path, newFile);
|
||||
_diskProvider.CreateFolder(new FileInfo(destinationFilename).DirectoryName);
|
||||
|
||||
_logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, destinationFilename);
|
||||
_diskProvider.MoveFile(episodeFile.Path, destinationFilename);
|
||||
|
||||
//Wrapped in Try/Catch to prevent this from causing issues with remote NAS boxes, the move worked, which is more important.
|
||||
try
|
||||
{
|
||||
_diskProvider.InheritFolderPermissions(newFile);
|
||||
_diskProvider.InheritFolderPermissions(destinationFilename);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_logger.Debug("Unable to apply folder permissions to: ", newFile);
|
||||
_logger.Debug("Unable to apply folder permissions to: ", destinationFilename);
|
||||
_logger.TraceException(ex.Message, ex);
|
||||
}
|
||||
|
||||
episodeFile.Path = newFile;
|
||||
_mediaFileService.Update(episodeFile);
|
||||
|
||||
var parsedEpisodeInfo = Parser.Parser.ParsePath(episodeFile.Path);
|
||||
parsedEpisodeInfo.Quality = episodeFile.Quality;
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
_messageAggregator.PublishEvent(new EpisodeDownloadedEvent(parsedEpisodeInfo, series));
|
||||
}
|
||||
episodeFile.Path = destinationFilename;
|
||||
|
||||
return episodeFile;
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public interface IImportDecisionEngineSpecification : IRejectWithReason
|
||||
{
|
||||
bool IsSatisfiedBy(LocalEpisode localEpisode);
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public interface IImportApprovedEpisodes
|
||||
{
|
||||
List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownloads = false);
|
||||
}
|
||||
|
||||
public class ImportApprovedEpisodes : IImportApprovedEpisodes
|
||||
{
|
||||
private readonly IMoveEpisodeFiles _episodeFileMover;
|
||||
private readonly MediaFileService _mediaFileService;
|
||||
private readonly DiskProvider _diskProvider;
|
||||
private readonly IMessageAggregator _messageAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportApprovedEpisodes(IMoveEpisodeFiles episodeFileMover,
|
||||
MediaFileService mediaFileService,
|
||||
DiskProvider diskProvider,
|
||||
IMessageAggregator messageAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_episodeFileMover = episodeFileMover;
|
||||
_mediaFileService = mediaFileService;
|
||||
_diskProvider = diskProvider;
|
||||
_messageAggregator = messageAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownload = false)
|
||||
{
|
||||
var qualifiedReports = GetQualifiedReports(decisions);
|
||||
var imported = new List<ImportDecision>();
|
||||
|
||||
foreach (var report in qualifiedReports)
|
||||
{
|
||||
var localEpisode = report.LocalEpisode;
|
||||
|
||||
try
|
||||
{
|
||||
if (imported.SelectMany(r => r.LocalEpisode.Episodes)
|
||||
.Select(e => e.Id)
|
||||
.ToList()
|
||||
.Intersect(localEpisode.Episodes.Select(e => e.Id))
|
||||
.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var episodeFile = new EpisodeFile();
|
||||
episodeFile.DateAdded = DateTime.UtcNow;
|
||||
episodeFile.SeriesId = localEpisode.Series.Id;
|
||||
episodeFile.Path = localEpisode.Path.CleanPath();
|
||||
episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path);
|
||||
episodeFile.Quality = localEpisode.Quality;
|
||||
episodeFile.SeasonNumber = localEpisode.SeasonNumber;
|
||||
episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanPath());
|
||||
episodeFile.Episodes = localEpisode.Episodes;
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
episodeFile = _episodeFileMover.MoveEpisodeFile(episodeFile, localEpisode);
|
||||
}
|
||||
|
||||
_mediaFileService.Add(episodeFile);
|
||||
_messageAggregator.PublishEvent(new EpisodeImportedEvent(episodeFile));
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.WarnException("Couldn't add report to download queue. " + localEpisode, e);
|
||||
}
|
||||
}
|
||||
|
||||
return imported;
|
||||
}
|
||||
|
||||
private List<ImportDecision> GetQualifiedReports(List<ImportDecision> decisions)
|
||||
{
|
||||
return decisions.Where(c => c.Approved)
|
||||
.OrderByDescending(c => c.LocalEpisode.Quality)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
28
NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs
Normal file
28
NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public class ImportDecision
|
||||
{
|
||||
public LocalEpisode LocalEpisode { get; private set; }
|
||||
public IEnumerable<string> Rejections { get; private set; }
|
||||
|
||||
public bool Approved
|
||||
{
|
||||
get
|
||||
{
|
||||
return !Rejections.Any();
|
||||
}
|
||||
}
|
||||
|
||||
public ImportDecision(LocalEpisode localEpisode, params string[] rejections)
|
||||
{
|
||||
LocalEpisode = localEpisode;
|
||||
Rejections = rejections.ToList();
|
||||
}
|
||||
}
|
||||
}
|
106
NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs
Normal file
106
NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs
Normal file
@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public interface IMakeImportDecision
|
||||
{
|
||||
List<ImportDecision> GetImportDecisions(IEnumerable<String> videoFiles, Series series);
|
||||
}
|
||||
|
||||
public class ImportDecisionMaker : IMakeImportDecision
|
||||
{
|
||||
private readonly IEnumerable<IRejectWithReason> _specifications;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportDecisionMaker(IEnumerable<IRejectWithReason> specifications, IParsingService parsingService, Logger logger)
|
||||
{
|
||||
_specifications = specifications;
|
||||
_parsingService = parsingService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(IEnumerable<String> videoFiles, Series series)
|
||||
{
|
||||
return GetDecisions(videoFiles, series).ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<ImportDecision> GetDecisions(IEnumerable<String> videoFiles, Series series)
|
||||
{
|
||||
foreach (var file in videoFiles)
|
||||
{
|
||||
ImportDecision decision = null;
|
||||
|
||||
try
|
||||
{
|
||||
var parsedEpisode = _parsingService.GetEpisodes(file, series);
|
||||
|
||||
if (parsedEpisode != null)
|
||||
{
|
||||
decision = GetDecision(parsedEpisode);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
parsedEpisode = new LocalEpisode();
|
||||
parsedEpisode.Path = file;
|
||||
|
||||
decision = new ImportDecision(parsedEpisode, "Unable to parse file");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.ErrorException("Couldn't process report.", e);
|
||||
}
|
||||
|
||||
if (decision != null)
|
||||
{
|
||||
yield return decision;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ImportDecision GetDecision(LocalEpisode localEpisode)
|
||||
{
|
||||
var reasons = _specifications.Select(c => EvaluateSpec(c, localEpisode))
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c));
|
||||
|
||||
return new ImportDecision(localEpisode, reasons.ToArray());
|
||||
}
|
||||
|
||||
private string EvaluateSpec(IRejectWithReason spec, LocalEpisode localEpisode)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(spec.RejectionReason))
|
||||
{
|
||||
throw new InvalidOperationException("[Need Rejection Text]");
|
||||
}
|
||||
|
||||
var generalSpecification = spec as IImportDecisionEngineSpecification;
|
||||
if (generalSpecification != null && !generalSpecification.IsSatisfiedBy(localEpisode))
|
||||
{
|
||||
return spec.RejectionReason;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//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 null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
public class NotAlreadyImportedSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public NotAlreadyImportedSpecification(IMediaFileService mediaFileService, Logger logger)
|
||||
{
|
||||
_mediaFileService = mediaFileService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Is Sample"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (_mediaFileService.Exists(localEpisode.Path))
|
||||
{
|
||||
_logger.Trace("[{0}] already exists in the database. skipping.", localEpisode.Path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
public class NotSampleSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public NotSampleSpecification(IDiskProvider diskProvider, IVideoFileInfoReader videoFileInfoReader, Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Sample"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (localEpisode.Series.SeriesType == SeriesTypes.Daily)
|
||||
{
|
||||
_logger.Trace("Daily Series, skipping sample check");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (localEpisode.SeasonNumber == 0)
|
||||
{
|
||||
_logger.Trace("Special, skipping sample check");
|
||||
return true;
|
||||
}
|
||||
|
||||
var size = _diskProvider.GetFileSize(localEpisode.Path);
|
||||
var runTime = _videoFileInfoReader.GetRunTime(localEpisode.Path);
|
||||
|
||||
if (size < Constants.IgnoreFileSize && runTime.TotalMinutes < 3)
|
||||
{
|
||||
_logger.Trace("[{0}] appears to be a sample.", localEpisode.Path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
public class UpgradeSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public UpgradeSpecification(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Is Sample"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && e.EpisodeFile.Value.Quality > localEpisode.Quality))
|
||||
{
|
||||
_logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", localEpisode.Path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -274,6 +274,13 @@
|
||||
<Compile Include="MediaFiles\Commands\CleanUpRecycleBinCommand.cs" />
|
||||
<Compile Include="MediaFiles\Commands\DownloadedEpisodesScanCommand.cs" />
|
||||
<Compile Include="MediaFiles\Commands\DiskScanCommand.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecision.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\IImportDecisionEngineSpecification.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMaker.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportApprovedEpisodes.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotAlreadyImportedSpecification.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecification.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecification.cs" />
|
||||
<Compile Include="MediaFiles\Events\EpisodeImportedEvent.cs" />
|
||||
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
|
||||
<Compile Include="Download\EpisodeGrabbedEvent.cs" />
|
||||
|
@ -6,11 +6,14 @@ namespace NzbDrone.Core.Parser.Model
|
||||
{
|
||||
public class LocalEpisode
|
||||
{
|
||||
//public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; }
|
||||
public Series Series { get; set; }
|
||||
|
||||
public List<Episode> Episodes { get; set; }
|
||||
|
||||
public QualityModel Quality { get; set; }
|
||||
|
||||
public int SeasonNumber { get { return Episodes.Select(c => c.SeasonNumber).Distinct().Single(); } }
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
@ -26,7 +26,6 @@ public ParsingService(IEpisodeService episodeService, ISeriesService seriesServi
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
public LocalEpisode GetEpisodes(string fileName, Series series)
|
||||
{
|
||||
var parsedEpisodeInfo = Parser.ParseTitle(fileName);
|
||||
@ -45,8 +44,10 @@ public LocalEpisode GetEpisodes(string fileName, Series series)
|
||||
|
||||
return new LocalEpisode
|
||||
{
|
||||
Series = series,
|
||||
Quality = parsedEpisodeInfo.Quality,
|
||||
Episodes = episodes,
|
||||
Path = fileName
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user