Fixed: Do not replace a file unless it contains the same episodes

pull/238/head
Mark McDowall 9 years ago
parent c08d8252ff
commit 0c6ca6971d

@ -37,5 +37,10 @@ namespace NzbDrone.Common.Extensions
{
return !source.All(predicate);
}
public static List<TResult> SelectList<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> predicate)
{
return source.Select(predicate).ToList();
}
}
}

@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class SameEpisodesSpecificationFixture : CoreTest<SameEpisodesSpecification>
{
private List<Episode> _episodes;
[SetUp]
public void Setup()
{
_episodes = Builder<Episode>.CreateListOfSize(2)
.All()
.With(e => e.EpisodeFileId = 1)
.BuildList();
}
private void GivenEpisodesInFile(List<Episode> episodes)
{
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(episodes);
}
[Test]
public void should_not_upgrade_when_new_release_contains_less_episodes()
{
GivenEpisodesInFile(_episodes);
Subject.IsSatisfiedBy(new List<Episode> { _episodes.First() }).Should().BeFalse();
}
[Test]
public void should_upgrade_when_new_release_contains_more_episodes()
{
GivenEpisodesInFile(new List<Episode> { _episodes.First() });
Subject.IsSatisfiedBy(_episodes).Should().BeTrue();
}
[Test]
public void should_upgrade_when_new_release_contains_the_same_episodes()
{
GivenEpisodesInFile(_episodes);
Subject.IsSatisfiedBy(_episodes).Should().BeTrue();
}
[Test]
public void should_upgrade_when_release_contains_the_same_episodes_as_multiple_files()
{
var episodes = Builder<Episode>.CreateListOfSize(2)
.BuildList();
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodesByFileId(episodes.First().EpisodeFileId))
.Returns(new List<Episode> { episodes.First() });
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodesByFileId(episodes.Last().EpisodeFileId))
.Returns(new List<Episode> { episodes.Last() });
Subject.IsSatisfiedBy(episodes).Should().BeTrue();
}
}
}

@ -147,6 +147,7 @@
<Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\SeriesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\SameEpisodesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\UpgradeDiskSpecificationFixture.cs" />
<Compile Include="Download\CompletedDownloadServiceFixture.cs" />
<Compile Include="Download\DownloadApprovedReportsTests\DownloadApprovedFixture.cs" />

@ -30,6 +30,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase(@"C:\Test\Series\Season 01\1 Pilot (1080p HD).mkv", 1, 1)]
[TestCase(@"C:\Test\Series\Season 1\02 Honor Thy Father (1080p HD).m4v", 1, 2)]
[TestCase(@"C:\Test\Series\Season 1\2 Honor Thy Father (1080p HD).m4v", 1, 2)]
// [TestCase(@"C:\CSI.NY.S02E04.720p.WEB-DL.DD5.1.H.264\73696S02-04.mkv", 2, 4)] //Gets treated as S01E04 (because it gets parsed as anime)
public void should_parse_from_path(string path, int season, int episode)
{
var result = Parser.Parser.ParsePath(path.AsOsAgnostic());

@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.DecisionEngine
{
public class SameEpisodesSpecification
{
private readonly IEpisodeService _episodeService;
public SameEpisodesSpecification(IEpisodeService episodeService)
{
_episodeService = episodeService;
}
public bool IsSatisfiedBy(List<Episode> episodes)
{
var episodeIds = episodes.SelectList(e => e.Id);
var episodeFileIds = episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFileId).Distinct();
foreach (var episodeFileId in episodeFileIds)
{
var episodesInFile = _episodeService.GetEpisodesByFileId(episodeFileId);
if (episodesInFile.Select(e => e.Id).Except(episodeIds).Any())
{
return false;
}
}
return true;
}
}
}

@ -0,0 +1,31 @@
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class SameEpisodesGrabSpecification : IDecisionEngineSpecification
{
private readonly SameEpisodesSpecification _sameEpisodesSpecification;
private readonly Logger _logger;
public SameEpisodesGrabSpecification(SameEpisodesSpecification sameEpisodesSpecification, Logger logger)
{
_sameEpisodesSpecification = sameEpisodesSpecification;
_logger = logger;
}
public RejectionType Type { get { return RejectionType.Permanent; } }
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (_sameEpisodesSpecification.IsSatisfiedBy(subject.Episodes))
{
return Decision.Accept();
}
_logger.Debug("Episode file on disk contains more episodes than this release contains");
return Decision.Reject("Episode file on disk contains more episodes than this release contains");
}
}
}

@ -0,0 +1,31 @@
using NLog;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
{
public class SameEpisodesImportSpecification : IImportDecisionEngineSpecification
{
private readonly SameEpisodesSpecification _sameEpisodesSpecification;
private readonly Logger _logger;
public SameEpisodesImportSpecification(SameEpisodesSpecification sameEpisodesSpecification, Logger logger)
{
_sameEpisodesSpecification = sameEpisodesSpecification;
_logger = logger;
}
public RejectionType Type { get { return RejectionType.Permanent; } }
public Decision IsSatisfiedBy(LocalEpisode localEpisode)
{
if (_sameEpisodesSpecification.IsSatisfiedBy(localEpisode.Episodes))
{
return Decision.Accept();
}
_logger.Debug("Episode file on disk contains more episodes than this file contains");
return Decision.Reject("Episode file on disk contains more episodes than this file contains");
}
}
}

@ -281,6 +281,7 @@
<Compile Include="DecisionEngine\QualityUpgradableSpecification.cs" />
<Compile Include="DecisionEngine\Rejection.cs" />
<Compile Include="DecisionEngine\RejectionType.cs" />
<Compile Include="DecisionEngine\SameEpisodesSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\AcceptableSizeSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\BlacklistSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\AnimeVersionUpgradeSpecification.cs" />
@ -303,6 +304,7 @@
<Compile Include="DecisionEngine\Specifications\Search\SeriesSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\Search\SingleEpisodeSearchMatchSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\Search\TorrentSeedingSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\SameEpisodesGrabSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\UpgradeDiskSpecification.cs" />
<Compile Include="DiskSpace\DiskSpace.cs" />
<Compile Include="DiskSpace\DiskSpaceService.cs" />
@ -582,6 +584,7 @@
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecification.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecification.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecification.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\SameEpisodesImportSpecification.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecification.cs" />
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
<Compile Include="MediaFiles\Events\EpisodeFileAddedEvent.cs" />

Loading…
Cancel
Save