diff --git a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotExistingFileSpecificationFixture.cs b/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotExistingFileSpecificationFixture.cs new file mode 100644 index 000000000..15c5bf677 --- /dev/null +++ b/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotExistingFileSpecificationFixture.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FizzWare.NBuilder; +using FluentAssertions; +using Marr.Data; +using NUnit.Framework; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests +{ + [TestFixture] + public class NotExistingFileSpecificationFixture : CoreTest + { + private LocalEpisode _localEpisode; + + [SetUp] + public void Setup() + { + _localEpisode = new LocalEpisode + { + Path = @"C:\Test\30 Rock\30.rock.s01e01.avi", + Size = 100 + }; + } + + [Test] + public void should_return_false_if_path_and_size_are_the_same() + { + _localEpisode.Episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.EpisodeFileId = 1) + .With(e => e.EpisodeFile = new LazyLoaded( + new EpisodeFile + { + Path = @"C:\Test\30 Rock\30.rock.s01e01.avi", + Size = 100 + })) + .Build() + .ToList(); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); + } + + [Test] + public void should_return_false_if_filename_and_size_are_the_same() + { + _localEpisode.Episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.EpisodeFileId = 1) + .With(e => e.EpisodeFile = new LazyLoaded( + new EpisodeFile + { + Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi", + Size = 100 + })) + .Build() + .ToList(); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); + } + + [Test] + public void should_return_true_if_no_existing_file() + { + _localEpisode.Episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.EpisodeFileId = 0) + .Build() + .ToList(); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); + } + + [Test] + public void should_return_true_if_size_is_different() + { + _localEpisode.Episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.EpisodeFileId = 1) + .With(e => e.EpisodeFile = new LazyLoaded( + new EpisodeFile + { + Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi", + Size = 50 + })) + .Build() + .ToList(); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); + } + + [Test] + public void should_return_true_if_file_names_are_different() + { + _localEpisode.Episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.EpisodeFileId = 1) + .With(e => e.EpisodeFile = new LazyLoaded( + new EpisodeFile + { + Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.pilot.avi", + Size = 100 + })) + .Build() + .ToList(); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); + } + } +} diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index c8cb159d3..791bfd19b 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -147,6 +147,7 @@ + diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs index f1f16a577..19e31bfab 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using NLog; +using NzbDrone.Common; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; @@ -21,12 +22,17 @@ public class ImportDecisionMaker : IMakeImportDecision { private readonly IEnumerable _specifications; private readonly IParsingService _parsingService; + private readonly IDiskProvider _diskProvider; private readonly Logger _logger; - public ImportDecisionMaker(IEnumerable specifications, IParsingService parsingService, Logger logger) + public ImportDecisionMaker(IEnumerable specifications, + IParsingService parsingService, + IDiskProvider diskProvider, + Logger logger) { _specifications = specifications; _parsingService = parsingService; + _diskProvider = diskProvider; _logger = logger; } @@ -44,9 +50,10 @@ private IEnumerable GetDecisions(IEnumerable videoFiles, try { var parsedEpisode = _parsingService.GetEpisodes(file, series); - + if (parsedEpisode != null) { + parsedEpisode.Size = _diskProvider.GetFileSize(file); decision = GetDecision(parsedEpisode); } diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotExistingFileSpecification.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotExistingFileSpecification.cs new file mode 100644 index 000000000..d5a5e7eba --- /dev/null +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotExistingFileSpecification.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using NLog; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications +{ + public class NotExistingFileSpecification : IImportDecisionEngineSpecification + { + private readonly Logger _logger; + + public NotExistingFileSpecification(Logger logger) + { + _logger = logger; + } + + public string RejectionReason { get { return "Existing File"; } } + + public bool IsSatisfiedBy(LocalEpisode localEpisode) + { + var episodeFiles = localEpisode.Episodes.Where(e => e.EpisodeFileId > 0).Select(e => e.EpisodeFile.Value); + + foreach (var episodeFile in episodeFiles) + { + if (Path.GetFileName(episodeFile.Path) == Path.GetFileName(localEpisode.Path) && + episodeFile.Size == localEpisode.Size) + { + _logger.Trace("File is a match for an existing episode file: {0}", localEpisode.Path); + return false; + } + } + + return true; + } + } +} diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs index 871639811..6c8b8c152 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs @@ -39,10 +39,9 @@ public bool IsSatisfiedBy(LocalEpisode localEpisode) return true; } - var size = _diskProvider.GetFileSize(localEpisode.Path); var runTime = _videoFileInfoReader.GetRunTime(localEpisode.Path); - if (size < Constants.IgnoreFileSize && runTime.TotalMinutes < 3) + if (localEpisode.Size < Constants.IgnoreFileSize && runTime.TotalMinutes < 3) { _logger.Trace("[{0}] appears to be a sample.", localEpisode.Path); return false; diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 10ac72adb..e289f24d3 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -279,6 +279,7 @@ + diff --git a/NzbDrone.Core/Parser/Model/LocalEpisode.cs b/NzbDrone.Core/Parser/Model/LocalEpisode.cs index f78013a80..3936ca4b7 100644 --- a/NzbDrone.Core/Parser/Model/LocalEpisode.cs +++ b/NzbDrone.Core/Parser/Model/LocalEpisode.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NzbDrone.Core.Tv; using System.Linq; @@ -7,17 +8,20 @@ namespace NzbDrone.Core.Parser.Model public class LocalEpisode { public string Path { get; set; } - + public Int64 Size { get; set; } public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } - public Series Series { get; set; } - public List Episodes { get; set; } - public QualityModel Quality { get; set; } - - public int SeasonNumber { get { return Episodes.Select(c => c.SeasonNumber).Distinct().Single(); } } - + + public int SeasonNumber + { + get + { + return Episodes.Select(c => c.SeasonNumber).Distinct().Single(); + } + } + public override string ToString() { return Path;