diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateEpisodesFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateEpisodesFixture.cs index 699d75ff3..969720f15 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateEpisodesFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateEpisodesFixture.cs @@ -44,7 +44,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); Mocker.GetMock() .Verify(v => v.GetEpisodes(fileEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once()); @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, true); + Subject.Aggregate(localEpisode, null, true); Mocker.GetMock() .Verify(v => v.GetEpisodes(fileEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once()); @@ -82,7 +82,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); Mocker.GetMock() .Verify(v => v.GetEpisodes(fileEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once()); @@ -101,7 +101,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); Mocker.GetMock() .Verify(v => v.GetEpisodes(folderEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once()); @@ -120,7 +120,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); Mocker.GetMock() .Verify(v => v.GetEpisodes(fileEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once()); @@ -143,7 +143,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators .Setup(s => s.ParseSpecialEpisodeTitle(fileEpisodeInfo, It.IsAny(), _series)) .Returns(specialEpisodeInfo); - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); Mocker.GetMock() .Verify(v => v.GetEpisodes(specialEpisodeInfo, _series, localEpisode.SceneSource, null), Times.Once()); diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguageFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguageFixture.cs index 8fc911b03..253528207 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguageFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguageFixture.cs @@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators { _localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(Language.English); - Subject.Aggregate(_localEpisode, false).Language.Should().Be(_localEpisode.FileEpisodeInfo.Language); + Subject.Aggregate(_localEpisode, null, false).Language.Should().Be(_localEpisode.FileEpisodeInfo.Language); } [Test] @@ -45,7 +45,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators _localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(Language.English); _localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(Language.English); - Subject.Aggregate(_localEpisode, false).Language.Should().Be(_localEpisode.FolderEpisodeInfo.Language); + Subject.Aggregate(_localEpisode, null, false).Language.Should().Be(_localEpisode.FolderEpisodeInfo.Language); } [Test] @@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators _localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(Language.English); _localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(Language.English); - Subject.Aggregate(_localEpisode, false).Language.Should().Be(_localEpisode.DownloadClientEpisodeInfo.Language); + Subject.Aggregate(_localEpisode, null, false).Language.Should().Be(_localEpisode.DownloadClientEpisodeInfo.Language); } @@ -66,7 +66,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators _localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(Language.English); _localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(Language.French); - Subject.Aggregate(_localEpisode, false).Language.Should().Be(_localEpisode.FileEpisodeInfo.Language); + Subject.Aggregate(_localEpisode, null, false).Language.Should().Be(_localEpisode.FileEpisodeInfo.Language); } } } diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateQualityFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateQualityFixture.cs index 75588be1f..b7c218ebe 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateQualityFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateQualityFixture.cs @@ -3,6 +3,7 @@ using System.Linq; using FluentAssertions; using Moq; using NUnit.Framework; +using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality; using NzbDrone.Core.Parser.Model; @@ -12,11 +13,12 @@ using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators { [TestFixture] - public class AugmentQualityFixture : CoreTest + public class AggregateQualityFixture : CoreTest { private Mock _mediaInfoAugmenter; private Mock _fileExtensionAugmenter; private Mock _nameAugmenter; + private Mock _releaseNameAugmenter; [SetUp] public void Setup() @@ -24,15 +26,24 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators _mediaInfoAugmenter = new Mock(); _fileExtensionAugmenter = new Mock(); _nameAugmenter = new Mock(); + _releaseNameAugmenter = new Mock(); - _mediaInfoAugmenter.Setup(s => s.AugmentQuality(It.IsAny())) + _fileExtensionAugmenter.SetupGet(s => s.Order).Returns(1); + _nameAugmenter.SetupGet(s => s.Order).Returns(2); + _mediaInfoAugmenter.SetupGet(s => s.Order).Returns(4); + _releaseNameAugmenter.SetupGet(s => s.Order).Returns(5); + + _mediaInfoAugmenter.Setup(s => s.AugmentQuality(It.IsAny(), It.IsAny())) .Returns(AugmentQualityResult.ResolutionOnly(1080, Confidence.MediaInfo)); - _fileExtensionAugmenter.Setup(s => s.AugmentQuality(It.IsAny())) + _fileExtensionAugmenter.Setup(s => s.AugmentQuality(It.IsAny(), It.IsAny())) .Returns(new AugmentQualityResult(QualitySource.Television, Confidence.Fallback, 720, Confidence.Fallback, new Revision())); - _nameAugmenter.Setup(s => s.AugmentQuality(It.IsAny())) + _nameAugmenter.Setup(s => s.AugmentQuality(It.IsAny(), It.IsAny())) .Returns(new AugmentQualityResult(QualitySource.Television, Confidence.Default, 480, Confidence.Default, new Revision())); + + _releaseNameAugmenter.Setup(s => s.AugmentQuality(It.IsAny(), It.IsAny())) + .Returns(AugmentQualityResult.SourceOnly(QualitySource.Web, Confidence.MediaInfo)); } private void GivenAugmenters(params Mock[] mocks) @@ -44,14 +55,15 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators public void should_return_HDTV720_from_extension_when_other_augments_are_null() { var nullMock = new Mock(); - nullMock.Setup(s => s.AugmentQuality(It.IsAny())) - .Returns(l => null); + nullMock.Setup(s => s.AugmentQuality(It.IsAny(), It.IsAny())) + .Returns((l, d) => null); GivenAugmenters(_fileExtensionAugmenter, nullMock); - var result = Subject.Aggregate(new LocalEpisode(), false); + var result = Subject.Aggregate(new LocalEpisode(), null, false); - result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.Extension); + result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Extension); + result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Extension); result.Quality.Quality.Should().Be(Quality.HDTV720p); } @@ -60,9 +72,10 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators { GivenAugmenters(_fileExtensionAugmenter, _nameAugmenter); - var result = Subject.Aggregate(new LocalEpisode(), false); + var result = Subject.Aggregate(new LocalEpisode(), null, false); - result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.Name); + result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Name); + result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name); result.Quality.Quality.Should().Be(Quality.SDTV); } @@ -71,9 +84,10 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators { GivenAugmenters(_fileExtensionAugmenter, _mediaInfoAugmenter); - var result = Subject.Aggregate(new LocalEpisode(), false); + var result = Subject.Aggregate(new LocalEpisode(), null, false); - result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.MediaInfo); + result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Extension); + result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.MediaInfo); result.Quality.Quality.Should().Be(Quality.HDTV1080p); } @@ -82,10 +96,23 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators { GivenAugmenters(_nameAugmenter, _mediaInfoAugmenter); - var result = Subject.Aggregate(new LocalEpisode(), false); + var result = Subject.Aggregate(new LocalEpisode(), null, false); - result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.MediaInfo); + result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Name); + result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.MediaInfo); result.Quality.Quality.Should().Be(Quality.HDTV1080p); } + + [Test] + public void should_return_WEBDL480p_when_file_name_has_HDTV480p_but_release_name_indicates_webdl_source() + { + GivenAugmenters(_nameAugmenter, _releaseNameAugmenter); + + var result = Subject.Aggregate(new LocalEpisode(), new DownloadClientItem(), false); + + result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Name); + result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name); + result.Quality.Quality.Should().Be(Quality.WEBDL480p); + } } } diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseGroupFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseGroupFixture.cs index b3f774e96..3f234e2f2 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseGroupFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseGroupFixture.cs @@ -1,8 +1,6 @@  -using System.Collections.Generic; using FizzWare.NBuilder; using FluentAssertions; -using Moq; using NUnit.Framework; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators; using NzbDrone.Core.Parser.Model; @@ -36,7 +34,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); localEpisode.ReleaseGroup.Should().Be("Wizzy"); } @@ -54,7 +52,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); localEpisode.ReleaseGroup.Should().Be("Wizzy"); } @@ -74,7 +72,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); localEpisode.ReleaseGroup.Should().Be("Viva"); } @@ -94,7 +92,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); localEpisode.ReleaseGroup.Should().Be("Drone"); } @@ -114,7 +112,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators Series = _series }; - Subject.Aggregate(localEpisode, false); + Subject.Aggregate(localEpisode, null, false); localEpisode.ReleaseGroup.Should().Be("Wizzy"); } diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromMediaInfoFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromMediaInfoFixture.cs index eabd186ac..061f528f0 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromMediaInfoFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromMediaInfoFixture.cs @@ -18,7 +18,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators.Au .With(l => l.MediaInfo = null) .Build(); - Subject.AugmentQuality(localEpisode).Should().Be(null); + Subject.AugmentQuality(localEpisode, null).Should().Be(null); } [Test] @@ -32,7 +32,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators.Au .With(l => l.MediaInfo = mediaInfo) .Build(); - Subject.AugmentQuality(localEpisode).Should().Be(null); + Subject.AugmentQuality(localEpisode, null).Should().Be(null); } [TestCase(4096, 2160)] // True 4K @@ -59,7 +59,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators.Au .With(l => l.MediaInfo = mediaInfo) .Build(); - var result = Subject.AugmentQuality(localEpisode); + var result = Subject.AugmentQuality(localEpisode, null); result.Should().NotBe(null); result.Resolution.Should().Be(expectedResolution); diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromReleaseNameFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromReleaseNameFixture.cs new file mode 100644 index 000000000..1fcd8fb29 --- /dev/null +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromReleaseNameFixture.cs @@ -0,0 +1,79 @@ +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Download; +using NzbDrone.Core.Download.History; +using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality +{ + [TestFixture] + public class AugmentQualityFromReleaseNameFixture : CoreTest + { + private LocalEpisode _localEpisode; + private DownloadClientItem _downloadClientItem; + private ParsedEpisodeInfo _hdtvParsedEpisodeInfo; + private ParsedEpisodeInfo _webdlParsedEpisodeInfo; + + [SetUp] + public void Setup() + { + _hdtvParsedEpisodeInfo = Builder.CreateNew() + .With(p => p.Quality = + new QualityModel(Core.Qualities.Quality.HDTV720p)) + .Build(); + + _webdlParsedEpisodeInfo = Builder.CreateNew() + .With(p => p.Quality = + new QualityModel(Core.Qualities.Quality.WEBDL720p)) + .Build(); + + _localEpisode = Builder.CreateNew() + .Build(); + + _downloadClientItem = Builder.CreateNew() + .Build(); + } + + [Test] + public void should_return_null_if_download_client_item_is_null() + { + Subject.AugmentQuality(_localEpisode, null).Should().BeNull(); + } + + [Test] + public void should_return_null_if_no_grabbed_history() + { + Mocker.GetMock() + .Setup(s => s.GetLatestGrab(It.IsAny())) + .Returns((DownloadHistory)null); + + Subject.AugmentQuality(_localEpisode, _downloadClientItem).Should().BeNull(); + } + + [TestCase("Series.Title.S01E01.1080p.WEB.x264", QualitySource.Web, Confidence.Tag, 1080, Confidence.Tag)] + [TestCase("Series.Title.S01E01.WEB.x264", QualitySource.Web, Confidence.Tag, 480, Confidence.Fallback)] + [TestCase("Series.Title.S01E01.720p.x264", QualitySource.Television, Confidence.Fallback, 720, Confidence.Tag)] + public void should_return_augmented_quality(string title, QualitySource source, Confidence sourceConfidence, int resolution, Confidence resolutionConfidence) + { + Mocker.GetMock() + .Setup(s => s.GetLatestGrab(It.IsAny())) + .Returns(Builder.CreateNew() + .With(h => h.SourceTitle = title) + .Build() + ); + + var result = Subject.AugmentQuality(_localEpisode, _downloadClientItem); + + result.Should().NotBe(null); + result.Source.Should().Be(source); + result.SourceConfidence.Should().Be(sourceConfidence); + result.Resolution.Should().Be(resolution); + result.ResolutionConfidence.Should().Be(resolutionConfidence); + } + } +} diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs index fe24f2fba..fc1088b63 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs @@ -25,6 +25,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport { private List _videoFiles; private LocalEpisode _localEpisode; + private DownloadClientItem _downloadClientItem; private Series _series; private QualityModel _quality; @@ -92,8 +93,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport private void GivenAugmentationSuccess() { Mocker.GetMock() - .Setup(s => s.Augment(It.IsAny(), It.IsAny())) - .Callback((localEpisode, otherFiles) => + .Setup(s => s.Augment(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((localEpisode, downloadClientItem, otherFiles) => { localEpisode.Episodes = _localEpisode.Episodes; }); @@ -163,7 +164,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport GivenSpecifications(_pass1); Mocker.GetMock() - .Setup(c => c.Augment(It.IsAny(), It.IsAny())) + .Setup(c => c.Augment(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(); _videoFiles = new List @@ -178,7 +179,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport Subject.GetImportDecisions(_videoFiles, _series); Mocker.GetMock() - .Verify(c => c.Augment(It.IsAny(), It.IsAny()), Times.Exactly(_videoFiles.Count)); + .Verify(c => c.Augment(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(_videoFiles.Count)); ExceptionVerification.ExpectedErrors(3); } @@ -199,7 +200,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport var decisions = Subject.GetImportDecisions(_videoFiles, _series); Mocker.GetMock() - .Verify(c => c.Augment(It.IsAny(), It.IsAny()), Times.Exactly(_videoFiles.Count)); + .Verify(c => c.Augment(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(_videoFiles.Count)); decisions.Should().HaveCount(3); decisions.First().Rejections.Should().NotBeEmpty(); @@ -209,7 +210,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport public void should_return_a_decision_when_exception_is_caught() { Mocker.GetMock() - .Setup(c => c.Augment(It.IsAny(), It.IsAny())) + .Setup(c => c.Augment(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(); _videoFiles = new List diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecificationFixture.cs deleted file mode 100644 index 9d79515bc..000000000 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecificationFixture.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Collections.Generic; -using FizzWare.NBuilder; -using FluentAssertions; -using Marr.Data; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Download; -using NzbDrone.Core.History; -using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Profiles.Qualities; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications -{ - [TestFixture] - public class DifferentQualitySpecificationFixture : CoreTest - { - private LocalEpisode _localEpisode; - private DownloadClientItem _downloadClientItem; - - [SetUp] - public void Setup() - { - var qualityProfile = new QualityProfile - { - Cutoff = Quality.Bluray1080p.Id, - Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p) - }; - - var fakeSeries = Builder.CreateNew() - .With(c => c.QualityProfile = qualityProfile) - .Build(); - - _localEpisode = Builder.CreateNew() - .With(l => l.Quality = new QualityModel(Quality.Bluray1080p)) - .With(l => l.DownloadClientEpisodeInfo = new ParsedEpisodeInfo()) - .With(l => l.Series = fakeSeries) - .Build(); - - _downloadClientItem = Builder.CreateNew() - .Build(); - } - - private void GivenGrabbedEpisodeHistory(QualityModel quality) - { - var history = Builder.CreateListOfSize(1) - .TheFirst(1) - .With(h => h.Quality = quality) - .With(h => h.EventType = EpisodeHistoryEventType.Grabbed) - .BuildList(); - - Mocker.GetMock() - .Setup(s => s.FindByDownloadId(It.IsAny())) - .Returns(history); - } - - [Test] - public void should_be_accepted_if_no_download_client_item() - { - Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue(); - } - - [Test] - public void should_be_accepted_if_quality_does_not_match_for_full_season_pack() - { - GivenGrabbedEpisodeHistory(new QualityModel(Quality.SDTV)); - _localEpisode.DownloadClientEpisodeInfo.FullSeason = true; - - Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); - } - - [Test] - public void should_be_accepted_if_no_grabbed_episode_history() - { - Mocker.GetMock() - .Setup(s => s.FindByDownloadId(It.IsAny())) - .Returns(new List()); - - _localEpisode.Episodes = Builder.CreateListOfSize(1) - .TheFirst(1) - .With(e => e.EpisodeFileId = 0) - .BuildList(); - - Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); - } - - [Test] - public void should_be_accepted_if_quality_matches() - { - GivenGrabbedEpisodeHistory(_localEpisode.Quality); - - Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); - } - - [Test] - public void should_be_rejected_if_quality_does_not_match() - { - GivenGrabbedEpisodeHistory(new QualityModel(Quality.SDTV)); - - Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeFalse(); - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index ab73d8aa6..f800911da 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -73,7 +73,9 @@ namespace NzbDrone.Core.Test.ParserTests public void should_parse_quality_from_extension(string title) { Parser.Parser.ParseTitle(title).Quality.Quality.Should().NotBe(Quality.Unknown); - Parser.Parser.ParseTitle(title).Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.Extension); + Parser.Parser.ParseTitle(title).Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Extension); + Parser.Parser.ParseTitle(title).Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Extension); + } diff --git a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs index e74201383..da3c6aa37 100644 --- a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs @@ -366,13 +366,45 @@ namespace NzbDrone.Core.Test.ParserTests } } - [TestCase("Saturday.Night.Live.Vintage.S10E09.Eddie.Murphy.The.Honeydrippers.1080i.UPSCALE.HDTV.DD5.1.MPEG2-zebra")] [TestCase("Dexter - S01E01 - Title [HDTV-1080p]")] + [TestCase("Saturday.Night.Live.Vintage.S10E09.Eddie.Murphy.The.Honeydrippers.1080i.UPSCALE.HDTV.DD5.1.MPEG2-zebra")] + [TestCase("Series.Title.S01E01.Bluray720p")] + [TestCase("Series.Title.S01E01.Bluray1080p")] + [TestCase("Series.Title.S01E01.Bluray2160p")] + [TestCase("Series.Title.S01E01.848x480.dvd")] + [TestCase("Series.Title.S01E01.848x480.Bluray")] + [TestCase("Series.Title.S01E01.1280x720.Bluray")] + [TestCase("Series.Title.S01E01.1920x1080.Bluray")] + public void should_parse_full_quality_from_name(string title) + { + var result = QualityParser.ParseQuality(title); + + result.SourceDetectionSource.Should().Be(QualityDetectionSource.Name); + result.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name); + } + [TestCase("[CR] Sailor Moon - 004 [480p][48CE2D0F]")] + [TestCase("Series.Title.S01E01.848x480")] + [TestCase("Series.Title.S01E01.1280x720")] + [TestCase("Series.Title.S01E01.1920x1080")] + public void should_parse_resolution_from_name(string title) + { + var result = QualityParser.ParseQuality(title); + + result.SourceDetectionSource.Should().Be(QualityDetectionSource.Unknown); + result.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name); + } + [TestCase("White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA")] - public void should_parse_quality_from_name(string title) + [TestCase("Series.Title.S01E01.Bluray")] + [TestCase("Series.Title.S01E01.HD.TV")] + [TestCase("Series.Title.S01E01.SD.TV")] + public void should_parse_source_from_name(string title) { - QualityParser.ParseQuality(title).QualityDetectionSource.Should().Be(QualityDetectionSource.Name); + var result = QualityParser.ParseQuality(title); + + result.SourceDetectionSource.Should().Be(QualityDetectionSource.Name); + result.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Unknown); } [TestCase("Revolution.S01E02.Chained.Heat.mkv")] @@ -381,7 +413,20 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("[CR] Sailor Moon - 004 [48CE2D0F].avi")] public void should_parse_quality_from_extension(string title) { - QualityParser.ParseQuality(title).QualityDetectionSource.Should().Be(QualityDetectionSource.Extension); + var result = QualityParser.ParseQuality(title); + + result.SourceDetectionSource.Should().Be(QualityDetectionSource.Extension); + result.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Extension); + } + + [TestCase("Revolution.S01E02.Chained.Heat.1080p.mkv")] + [TestCase("Dexter - S01E01 - Title.720p.avi")] + public void should_parse_resolution_from_name_and_source_from_extension(string title) + { + var result = QualityParser.ParseQuality(title); + + result.SourceDetectionSource.Should().Be(QualityDetectionSource.Extension); + result.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name); } [TestCase("Series Title S04E87 REPACK 720p HDTV x264 aAF", true)] diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/AlreadyImportedSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/AlreadyImportedSpecification.cs index 2dcdeefee..00bad3ba3 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/AlreadyImportedSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/AlreadyImportedSpecification.cs @@ -84,7 +84,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications } } - // Only based on title because a release with the same title on another indexer/released at + // Only based on title because a release with the same title on another indexer/released at // a different time very likely has the exact same content and we don't need to also try it. if (release.Title.Equals(lastGrabbed.SourceTitle, StringComparison.InvariantCultureIgnoreCase)) diff --git a/src/NzbDrone.Core/Download/DownloadClientItem.cs b/src/NzbDrone.Core/Download/DownloadClientItem.cs index 23fd0f49e..ead3f4b48 100644 --- a/src/NzbDrone.Core/Download/DownloadClientItem.cs +++ b/src/NzbDrone.Core/Download/DownloadClientItem.cs @@ -13,7 +13,6 @@ namespace NzbDrone.Core.Download public string DownloadId { get; set; } public string Category { get; set; } public string Title { get; set; } - public long TotalSize { get; set; } public long RemainingSize { get; set; } public TimeSpan? RemainingTime { get; set; } diff --git a/src/NzbDrone.Core/Download/History/DownloadHistoryService.cs b/src/NzbDrone.Core/Download/History/DownloadHistoryService.cs index 666970da8..174cebb96 100644 --- a/src/NzbDrone.Core/Download/History/DownloadHistoryService.cs +++ b/src/NzbDrone.Core/Download/History/DownloadHistoryService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using NzbDrone.Common.Extensions; using NzbDrone.Core.History; using NzbDrone.Core.MediaFiles.Events; @@ -13,6 +14,7 @@ namespace NzbDrone.Core.Download.History { bool DownloadAlreadyImported(string downloadId); DownloadHistory GetLatestDownloadHistoryItem(string downloadId); + DownloadHistory GetLatestGrab(string downloadId); } public class DownloadHistoryService : IDownloadHistoryService, @@ -86,6 +88,12 @@ namespace NzbDrone.Core.Download.History return null; } + public DownloadHistory GetLatestGrab(string downloadId) + { + return _repository.FindByDownloadId(downloadId) + .FirstOrDefault(d => d.EventType == DownloadHistoryEventType.DownloadGrabbed); + } + public void Handle(EpisodeGrabbedEvent message) { // Don't store grabbed events for clients that don't download IDs diff --git a/src/NzbDrone.Core/Extras/Metadata/ExistingMetadataImporter.cs b/src/NzbDrone.Core/Extras/Metadata/ExistingMetadataImporter.cs index 8bd0178d1..aabce72aa 100644 --- a/src/NzbDrone.Core/Extras/Metadata/ExistingMetadataImporter.cs +++ b/src/NzbDrone.Core/Extras/Metadata/ExistingMetadataImporter.cs @@ -71,7 +71,7 @@ namespace NzbDrone.Core.Extras.Metadata try { - _aggregationService.Augment(localEpisode, false); + _aggregationService.Augment(localEpisode, null, false); } catch (AugmentingFailedException) { diff --git a/src/NzbDrone.Core/Extras/Others/ExistingOtherExtraImporter.cs b/src/NzbDrone.Core/Extras/Others/ExistingOtherExtraImporter.cs index c1d5bc376..e9093f2d0 100644 --- a/src/NzbDrone.Core/Extras/Others/ExistingOtherExtraImporter.cs +++ b/src/NzbDrone.Core/Extras/Others/ExistingOtherExtraImporter.cs @@ -56,7 +56,7 @@ namespace NzbDrone.Core.Extras.Others try { - _aggregationService.Augment(localEpisode, false); + _aggregationService.Augment(localEpisode, null, false); } catch (AugmentingFailedException) { diff --git a/src/NzbDrone.Core/Extras/Subtitles/ExistingSubtitleImporter.cs b/src/NzbDrone.Core/Extras/Subtitles/ExistingSubtitleImporter.cs index 2e6f950cb..068e0bba7 100644 --- a/src/NzbDrone.Core/Extras/Subtitles/ExistingSubtitleImporter.cs +++ b/src/NzbDrone.Core/Extras/Subtitles/ExistingSubtitleImporter.cs @@ -51,7 +51,7 @@ namespace NzbDrone.Core.Extras.Subtitles try { - _aggregationService.Augment(localEpisode, false); + _aggregationService.Augment(localEpisode, null, false); } catch (AugmentingFailedException) { diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/AggregationService.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/AggregationService.cs index 14ff63a98..0362b3e31 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/AggregationService.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/AggregationService.cs @@ -4,6 +4,7 @@ using System.IO; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators; using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.Parser.Model; @@ -12,7 +13,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation { public interface IAggregationService { - LocalEpisode Augment(LocalEpisode localEpisode, bool otherFiles); + LocalEpisode Augment(LocalEpisode localEpisode, DownloadClientItem downloadClientItem, bool otherFiles); } public class AggregationService : IAggregationService @@ -36,7 +37,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation _logger = logger; } - public LocalEpisode Augment(LocalEpisode localEpisode, bool otherFiles) + public LocalEpisode Augment(LocalEpisode localEpisode, DownloadClientItem downloadClientItem, bool otherFiles) { var isMediaFile = MediaFileExtensions.Extensions.Contains(Path.GetExtension(localEpisode.Path)); @@ -61,7 +62,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation { try { - augmenter.Aggregate(localEpisode, otherFiles); + augmenter.Aggregate(localEpisode, downloadClientItem, otherFiles); } catch (Exception ex) { diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateEpisodes.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateEpisodes.cs index d64f5f506..a446d3356 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateEpisodes.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateEpisodes.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using NzbDrone.Core.Download; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -15,7 +16,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators _parsingService = parsingService; } - public LocalEpisode Aggregate(LocalEpisode localEpisode, bool otherFiles) + public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem, bool otherFiles) { localEpisode.Episodes = GetEpisodes(localEpisode, otherFiles); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguage.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguage.cs index 11fc758ca..34aebc34d 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguage.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguage.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Core.Download; using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; @@ -15,7 +16,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators _logger = logger; } - public LocalEpisode Aggregate(LocalEpisode localEpisode, bool otherFiles) + public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem, bool otherFiles) { // Get languages in preferred order, download client item, folder and finally file. // Non-English languages will be preferred later, in the event there is a conflict diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateQuality.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateQuality.cs index 9679d1132..0cd03b8a9 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateQuality.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateQuality.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -19,55 +20,65 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators _logger = logger; } - public LocalEpisode Aggregate(LocalEpisode localEpisode, bool otherFiles) + public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem, bool otherFiles) { - var augmentedQualities = _augmentQualities.Select(a => a.AugmentQuality(localEpisode)) + var augmentedQualities = _augmentQualities.OrderBy(a => a.Order) + .Select(a => a.AugmentQuality(localEpisode, downloadClientItem)) .Where(a => a != null) - .OrderBy(a => a.SourceConfidence); + .ToList(); var source = QualitySource.Unknown; var sourceConfidence = Confidence.Default; var resolution = 0; var resolutionConfidence = Confidence.Default; - var revison = new Revision(); + var revision = new Revision(); foreach (var augmentedQuality in augmentedQualities) { - if (augmentedQuality.Source > source || + if (source == QualitySource.Unknown || augmentedQuality.SourceConfidence > sourceConfidence && augmentedQuality.Source != QualitySource.Unknown) { source = augmentedQuality.Source; sourceConfidence = augmentedQuality.SourceConfidence; } - if (augmentedQuality.Resolution > resolution || + if (resolution == 0 || augmentedQuality.ResolutionConfidence > resolutionConfidence && augmentedQuality.Resolution > 0) { resolution = augmentedQuality.Resolution; resolutionConfidence = augmentedQuality.ResolutionConfidence; } - if (augmentedQuality.Revision != null && augmentedQuality.Revision > revison) + if (augmentedQuality.Revision != null && augmentedQuality.Revision > revision) { - revison = augmentedQuality.Revision; + revision = augmentedQuality.Revision; } } _logger.Trace("Finding quality. Source: {0}. Resolution: {1}", source, resolution); - var quality = new QualityModel(QualityFinder.FindBySourceAndResolution(source, resolution), revison); + var quality = new QualityModel(QualityFinder.FindBySourceAndResolution(source, resolution), revision); if (resolutionConfidence == Confidence.MediaInfo) { - quality.QualityDetectionSource = QualityDetectionSource.MediaInfo; + quality.ResolutionDetectionSource = QualityDetectionSource.MediaInfo; } - else if (sourceConfidence == Confidence.Fallback || resolutionConfidence == Confidence.Fallback) + else if (resolutionConfidence == Confidence.Fallback) { - quality.QualityDetectionSource = QualityDetectionSource.Extension; + quality.ResolutionDetectionSource = QualityDetectionSource.Extension; } else { - quality.QualityDetectionSource = QualityDetectionSource.Name; + quality.ResolutionDetectionSource = QualityDetectionSource.Name; + } + + if (sourceConfidence == Confidence.Fallback) + { + quality.SourceDetectionSource = QualityDetectionSource.Extension; + } + else + { + quality.SourceDetectionSource = QualityDetectionSource.Name; } _logger.Debug("Using quality: {0}", quality); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseGroup.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseGroup.cs index 470269487..2126120a4 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseGroup.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseGroup.cs @@ -1,11 +1,12 @@ using NzbDrone.Common.Extensions; +using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators { public class AggregateReleaseGroup : IAggregateLocalEpisode { - public LocalEpisode Aggregate(LocalEpisode localEpisode, bool otherFiles) + public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem, bool otherFiles) { // Prefer ReleaseGroup from DownloadClient/Folder if they're not a season pack var releaseGroup = GetReleaseGroup(localEpisode.DownloadClientEpisodeInfo, true); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromDownloadClientItem.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromDownloadClientItem.cs index 13da77969..6b7288c17 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromDownloadClientItem.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromDownloadClientItem.cs @@ -1,10 +1,14 @@ +using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality { public class AugmentQualityFromDownloadClientItem : IAugmentQuality { - public AugmentQualityResult AugmentQuality(LocalEpisode localEpisode) + public int Order => 3; + + public AugmentQualityResult AugmentQuality(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { var quality = localEpisode.DownloadClientEpisodeInfo?.Quality; @@ -13,10 +17,18 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment return null; } + var sourceConfidence = quality.SourceDetectionSource == QualityDetectionSource.Name + ? Confidence.Tag + : Confidence.Fallback; + + var resolutionConfidence = quality.ResolutionDetectionSource == QualityDetectionSource.Name + ? Confidence.Tag + : Confidence.Fallback; + return new AugmentQualityResult(quality.Quality.Source, - Confidence.Tag, + sourceConfidence, quality.Quality.Resolution, - Confidence.Tag, + resolutionConfidence, quality.Revision); } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromFileName.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromFileName.cs index 53475b5bb..9ed47d715 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromFileName.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromFileName.cs @@ -1,3 +1,4 @@ +using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -5,7 +6,9 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment { public class AugmentQualityFromFileName : IAugmentQuality { - public AugmentQualityResult AugmentQuality(LocalEpisode localEpisode) + public int Order => 1; + + public AugmentQualityResult AugmentQuality(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { var quality = localEpisode.FileEpisodeInfo?.Quality; @@ -14,14 +17,18 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment return null; } - var confidence = quality.QualityDetectionSource == QualityDetectionSource.Extension - ? Confidence.Fallback - : Confidence.Tag; + var sourceConfidence = quality.SourceDetectionSource == QualityDetectionSource.Name + ? Confidence.Tag + : Confidence.Fallback; + + var resolutionConfidence = quality.ResolutionDetectionSource == QualityDetectionSource.Name + ? Confidence.Tag + : Confidence.Fallback; return new AugmentQualityResult(quality.Quality.Source, - confidence, + sourceConfidence, quality.Quality.Resolution, - confidence, + resolutionConfidence, quality.Revision); } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromFolder.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromFolder.cs index e59f3dbdc..6594e6e8f 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromFolder.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromFolder.cs @@ -1,10 +1,14 @@ +using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality { public class AugmentQualityFromFolder : IAugmentQuality { - public AugmentQualityResult AugmentQuality(LocalEpisode localEpisode) + public int Order => 2; + + public AugmentQualityResult AugmentQuality(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { var quality = localEpisode.FolderEpisodeInfo?.Quality; @@ -13,10 +17,18 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment return null; } + var sourceConfidence = quality.SourceDetectionSource == QualityDetectionSource.Name + ? Confidence.Tag + : Confidence.Fallback; + + var resolutionConfidence = quality.ResolutionDetectionSource == QualityDetectionSource.Name + ? Confidence.Tag + : Confidence.Fallback; + return new AugmentQualityResult(quality.Quality.Source, - Confidence.Tag, + sourceConfidence, quality.Quality.Resolution, - Confidence.Tag, + resolutionConfidence, quality.Revision); } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromMediaInfo.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromMediaInfo.cs index 958e226f8..b728d0ddb 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromMediaInfo.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromMediaInfo.cs @@ -1,10 +1,13 @@ +using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality { public class AugmentQualityFromMediaInfo : IAugmentQuality { - public AugmentQualityResult AugmentQuality(LocalEpisode localEpisode) + public int Order => 4; + + public AugmentQualityResult AugmentQuality(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.MediaInfo == null) { diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromReleaseName.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromReleaseName.cs new file mode 100644 index 000000000..f6bcc1dae --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/AugmentQualityFromReleaseName.cs @@ -0,0 +1,48 @@ +using NzbDrone.Core.Download; +using NzbDrone.Core.Download.History; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; + +namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality +{ + public class AugmentQualityFromReleaseName : IAugmentQuality + { + public int Order => 5; + + private readonly IDownloadHistoryService _downloadHistoryService; + + public AugmentQualityFromReleaseName(IDownloadHistoryService downloadHistoryService) + { + _downloadHistoryService = downloadHistoryService; + } + + public AugmentQualityResult AugmentQuality(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + { + // Don't try to augment if we can't lookup the grabbed history by downloadId + if (downloadClientItem == null) + { + return null; + } + + var history = _downloadHistoryService.GetLatestGrab(downloadClientItem.DownloadId); + + if (history == null) + { + return null; + } + + var historyQuality = QualityParser.ParseQuality(history.SourceTitle); + + var sourceConfidence = historyQuality.SourceDetectionSource == QualityDetectionSource.Name + ? Confidence.Tag + : Confidence.Fallback; + + var resolutionConfidence = historyQuality.ResolutionDetectionSource == QualityDetectionSource.Name + ? Confidence.Tag + : Confidence.Fallback; + + return new AugmentQualityResult(historyQuality.Quality.Source, sourceConfidence, historyQuality.Quality.Resolution, resolutionConfidence, historyQuality.Revision); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/IAugmentQuality.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/IAugmentQuality.cs index 6bf464321..a8b722320 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/IAugmentQuality.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Quality/IAugmentQuality.cs @@ -1,9 +1,11 @@ +using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augmenters.Quality { public interface IAugmentQuality { - AugmentQualityResult AugmentQuality(LocalEpisode localEpisode); + int Order { get; } + AugmentQualityResult AugmentQuality(LocalEpisode localEpisode, DownloadClientItem downloadClientItem); } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/IAggregateLocalEpisode.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/IAggregateLocalEpisode.cs index 452cac2a8..c6374744e 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/IAggregateLocalEpisode.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/IAggregateLocalEpisode.cs @@ -1,9 +1,10 @@ +using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators { public interface IAggregateLocalEpisode { - LocalEpisode Aggregate(LocalEpisode localEpisode, bool otherFiles); + LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem, bool otherFiles); } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs index 96db342f9..58f3375e9 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs @@ -101,7 +101,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport try { - _aggregationService.Augment(localEpisode, otherFiles); + _aggregationService.Augment(localEpisode, downloadClientItem, otherFiles); if (localEpisode.Episodes.Empty()) { diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs index fa99ee953..a845a3396 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs @@ -319,7 +319,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual localEpisode.FolderEpisodeInfo = Parser.Parser.ParseTitle(file.FolderName); } - localEpisode = _aggregationService.Augment(localEpisode, false); + localEpisode = _aggregationService.Augment(localEpisode, trackedDownload?.DownloadItem, false); // Apply the user-chosen values. localEpisode.Series = series; diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecification.cs deleted file mode 100644 index 3b33868f4..000000000 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecification.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Linq; -using NLog; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; -using NzbDrone.Core.History; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Qualities; - -namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications -{ - public class DifferentQualitySpecification : IImportDecisionEngineSpecification - { - private readonly IHistoryService _historyService; - private readonly Logger _logger; - - public DifferentQualitySpecification(IHistoryService historyService, Logger logger) - { - _historyService = historyService; - _logger = logger; - } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) - { - if (downloadClientItem == null) - { - _logger.Debug("No download client item, skipping"); - return Decision.Accept(); - } - - if (localEpisode.DownloadClientEpisodeInfo?.FullSeason == true) - { - _logger.Debug("Full season download, skipping"); - return Decision.Accept(); - } - - var grabbedEpisodeHistory = _historyService.FindByDownloadId(downloadClientItem.DownloadId) - .OrderByDescending(h => h.Date) - .FirstOrDefault(h => h.EventType == EpisodeHistoryEventType.Grabbed); - - if (grabbedEpisodeHistory == null) - { - _logger.Debug("No grabbed history for this download item, skipping"); - return Decision.Accept(); - } - - var qualityComparer = new QualityModelComparer(localEpisode.Series.QualityProfile); - var qualityCompare = qualityComparer.Compare(localEpisode.Quality, grabbedEpisodeHistory.Quality); - - if (qualityCompare != 0) - { - _logger.Debug("Quality of file ({0}) does not match quality of grabbed release ({1})", localEpisode.Quality, grabbedEpisodeHistory.Quality); - return Decision.Reject("Quality of file ({0}) does not match quality of grabbed release ({1})", localEpisode.Quality, grabbedEpisodeHistory.Quality); - } - - return Decision.Accept(); - } - } -} diff --git a/src/NzbDrone.Core/Parser/QualityParser.cs b/src/NzbDrone.Core/Parser/QualityParser.cs index 8b09782df..d6f5129d6 100644 --- a/src/NzbDrone.Core/Parser/QualityParser.cs +++ b/src/NzbDrone.Core/Parser/QualityParser.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using System.Web.UI; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation; @@ -72,7 +73,8 @@ namespace NzbDrone.Core.Parser try { result.Quality = MediaFileExtensions.GetQualityForExtension(Path.GetExtension(name)); - result.QualityDetectionSource = QualityDetectionSource.Extension; + result.SourceDetectionSource = QualityDetectionSource.Extension; + result.ResolutionDetectionSource = QualityDetectionSource.Extension; } catch (ArgumentException) { @@ -91,7 +93,10 @@ namespace NzbDrone.Core.Parser if (RawHDRegex.IsMatch(normalizedName)) { + result.SourceDetectionSource = QualityDetectionSource.Name; + result.ResolutionDetectionSource = QualityDetectionSource.Name; result.Quality = Quality.RAWHD; + return result; } @@ -101,8 +106,15 @@ namespace NzbDrone.Core.Parser var codecRegex = CodecRegex.Match(normalizedName); var remuxMatch = RemuxRegex.IsMatch(normalizedName); + if (resolution != Resolution.Unknown) + { + result.ResolutionDetectionSource = QualityDetectionSource.Name; + } + if (sourceMatch != null && sourceMatch.Success) { + result.SourceDetectionSource = QualityDetectionSource.Name; + if (sourceMatch.Groups["bluray"].Success) { if (codecRegex.Groups["xvid"].Success || codecRegex.Groups["divx"].Success) @@ -267,6 +279,7 @@ namespace NzbDrone.Core.Parser if (HighDefPdtvRegex.IsMatch(normalizedName)) { + result.ResolutionDetectionSource = QualityDetectionSource.Name; result.Quality = Quality.HDTV720p; return result; } @@ -276,26 +289,33 @@ namespace NzbDrone.Core.Parser } } - // Anime Bluray matching if (AnimeBlurayRegex.Match(normalizedName).Success) { + result.SourceDetectionSource = QualityDetectionSource.Name; + if (resolution == Resolution.R360P || resolution == Resolution.R480P || resolution == Resolution.R576p || normalizedName.ContainsIgnoreCase("480p")) { + result.ResolutionDetectionSource = QualityDetectionSource.Name; result.Quality = Quality.DVD; + return result; } if (resolution == Resolution.R1080p || normalizedName.ContainsIgnoreCase("1080p")) { + result.ResolutionDetectionSource = QualityDetectionSource.Name; result.Quality = remuxMatch ? Quality.Bluray1080pRemux : Quality.Bluray1080p; + return result; } if (resolution == Resolution.R2160p || normalizedName.ContainsIgnoreCase("2160p")) { + result.ResolutionDetectionSource = QualityDetectionSource.Name; result.Quality = remuxMatch ? Quality.Bluray2160pRemux : Quality.Bluray2160p; + return result; } @@ -310,85 +330,167 @@ namespace NzbDrone.Core.Parser return result; } - if (resolution == Resolution.R2160p) + if (resolution != Resolution.Unknown) { - result.Quality = remuxMatch ? Quality.Bluray2160pRemux : Quality.HDTV2160p; - return result; - } + var source = QualitySource.Unknown; - if (resolution == Resolution.R1080p) - { - result.Quality = remuxMatch ? Quality.Bluray1080pRemux : Quality.HDTV1080p; - return result; - } + if (remuxMatch) + { + result.SourceDetectionSource = QualityDetectionSource.Name; + source = QualitySource.BlurayRaw; + } + else + { + var quality = MediaFileExtensions.GetQualityForExtension(Path.GetExtension(name)); - if (resolution == Resolution.R720p) - { - result.Quality = Quality.HDTV720p; - return result; - } + if (quality != Quality.Unknown) + { + result.SourceDetectionSource = QualityDetectionSource.Extension; + source = quality.Source; + } + } - if (resolution == Resolution.R360P || resolution == Resolution.R480P) - { - result.Quality = Quality.SDTV; - return result; + if (resolution == Resolution.R2160p) + { + result.ResolutionDetectionSource = QualityDetectionSource.Name; + + result.Quality = source == QualitySource.Unknown + ? Quality.HDTV2160p + : QualityFinder.FindBySourceAndResolution(source, 2160); + + return result; + } + + if (resolution == Resolution.R1080p) + { + result.ResolutionDetectionSource = QualityDetectionSource.Name; + + result.Quality = source == QualitySource.Unknown + ? Quality.HDTV1080p + : QualityFinder.FindBySourceAndResolution(source, 1080); + + return result; + } + + if (resolution == Resolution.R720p) + { + result.ResolutionDetectionSource = QualityDetectionSource.Name; + + result.Quality = source == QualitySource.Unknown + ? Quality.HDTV720p + : QualityFinder.FindBySourceAndResolution(source, 720); + + return result; + } + + if (resolution == Resolution.R360P || resolution == Resolution.R480P) + { + result.ResolutionDetectionSource = QualityDetectionSource.Name; + + result.Quality = source == QualitySource.Unknown + ? Quality.SDTV + : QualityFinder.FindBySourceAndResolution(source, 480); + + return result; + } } + if (codecRegex.Groups["x264"].Success) { result.Quality = Quality.SDTV; + return result; } if (normalizedName.Contains("848x480")) { + result.ResolutionDetectionSource = QualityDetectionSource.Name; + if (normalizedName.Contains("dvd")) { + result.SourceDetectionSource = QualityDetectionSource.Name; result.Quality = Quality.DVD; } + else if (normalizedName.ContainsIgnoreCase("bluray")) + { + result.SourceDetectionSource = QualityDetectionSource.Name; + result.Quality = Quality.Bluray480p; + } + else + { + result.Quality = Quality.SDTV; + } - result.Quality = Quality.SDTV; + return result; } if (normalizedName.ContainsIgnoreCase("1280x720")) { + result.ResolutionDetectionSource = QualityDetectionSource.Name; + if (normalizedName.ContainsIgnoreCase("bluray")) { + result.SourceDetectionSource = QualityDetectionSource.Name; result.Quality = Quality.Bluray720p; } + else + { + result.Quality = Quality.HDTV720p; + } - result.Quality = Quality.HDTV720p; + return result; } if (normalizedName.ContainsIgnoreCase("1920x1080")) { + result.ResolutionDetectionSource = QualityDetectionSource.Name; + if (normalizedName.ContainsIgnoreCase("bluray")) { + result.SourceDetectionSource = QualityDetectionSource.Name; result.Quality = Quality.Bluray1080p; } + else + { + result.Quality = Quality.HDTV1080p; + } - result.Quality = Quality.HDTV1080p; + return result; } if (normalizedName.ContainsIgnoreCase("bluray720p")) { + result.SourceDetectionSource = QualityDetectionSource.Name; + result.ResolutionDetectionSource = QualityDetectionSource.Name; result.Quality = Quality.Bluray720p; + + return result; } if (normalizedName.ContainsIgnoreCase("bluray1080p")) { + result.SourceDetectionSource = QualityDetectionSource.Name; + result.ResolutionDetectionSource = QualityDetectionSource.Name; result.Quality = Quality.Bluray1080p; + + return result; } if (normalizedName.ContainsIgnoreCase("bluray2160p")) { + result.SourceDetectionSource = QualityDetectionSource.Name; + result.ResolutionDetectionSource = QualityDetectionSource.Name; result.Quality = Quality.Bluray2160p; + + return result; } var otherSourceMatch = OtherSourceMatch(normalizedName); if (otherSourceMatch != Quality.Unknown) { + result.SourceDetectionSource = QualityDetectionSource.Name; result.Quality = otherSourceMatch; } diff --git a/src/NzbDrone.Core/Qualities/QualityDetectionSource.cs b/src/NzbDrone.Core/Qualities/QualityDetectionSource.cs index 3f7695214..52a4f1f8b 100644 --- a/src/NzbDrone.Core/Qualities/QualityDetectionSource.cs +++ b/src/NzbDrone.Core/Qualities/QualityDetectionSource.cs @@ -2,6 +2,7 @@ { public enum QualityDetectionSource { + Unknown, Name, Extension, MediaInfo diff --git a/src/NzbDrone.Core/Qualities/QualityModel.cs b/src/NzbDrone.Core/Qualities/QualityModel.cs index 610dc1211..883c26a85 100644 --- a/src/NzbDrone.Core/Qualities/QualityModel.cs +++ b/src/NzbDrone.Core/Qualities/QualityModel.cs @@ -11,8 +11,11 @@ namespace NzbDrone.Core.Qualities public Revision Revision { get; set; } [JsonIgnore] - public QualityDetectionSource QualityDetectionSource { get; set; } - + public QualityDetectionSource SourceDetectionSource { get; set; } + + [JsonIgnore] + public QualityDetectionSource ResolutionDetectionSource { get; set; } + public QualityModel() : this(Quality.Unknown, new Revision()) {