From cbcf3d1058f47a87c43658084eafac13e7e0889f Mon Sep 17 00:00:00 2001 From: Qstick Date: Sun, 5 Feb 2023 17:09:37 -0600 Subject: [PATCH] New: Custom Format Updates (#8067) --- frontend/src/Activity/History/HistoryRow.js | 6 +- .../InteractiveImportModalContent.js | 9 + .../Interactive/InteractiveImportRow.css | 8 +- .../Interactive/InteractiveImportRow.js | 30 ++- frontend/src/Movie/History/MovieHistoryRow.js | 6 +- .../Number/formatCustomFormatScore.js | 5 +- ...Fixture.cs => CustomFormatsTestHelpers.cs} | 4 +- ...matAllowedByProfileSpecificationFixture.cs | 14 +- .../CutoffSpecificationFixture.cs | 8 +- .../HistorySpecificationFixture.cs | 14 +- .../PrioritizeDownloadDecisionFixture.cs | 4 +- .../QualityUpgradeSpecificationFixture.cs | 4 +- .../QueueSpecificationFixture.cs | 6 +- .../UpgradeDiskSpecificationFixture.cs | 4 +- .../Aggregators/AggregateLanguagesFixture.cs | 112 +++++++++ .../RemovePendingFixture.cs | 1 + .../MovieRepositoryFixture.cs | 2 +- .../ParserTests/ParserFixture.cs | 12 + .../AugmentMovieInfoFixture.cs | 28 --- .../AugmentWithFileSizeFixture.cs | 23 -- .../AugmentWithHistoryFixture.cs | 109 --------- .../AugmentWithMediaInfoFixture.cs | 90 -------- .../AugmentWithOriginalLanguageFixture.cs | 29 --- .../AugmentWithParsedMovieInfo.cs | 77 ------- .../AugmentWithReleaseInfoFixture.cs | 82 ------- .../ParsingServiceTests/MapFixture.cs | 15 -- .../ParserTests/QualityParserFixture.cs | 12 - .../Profiles/ProfileRepositoryFixture.cs | 2 +- .../Profiles/ProfileServiceFixture.cs | 4 +- .../CustomFormatCalculationService.cs | 214 +++++++++++------- .../CustomFormats/CustomFormatInput.cs | 22 ++ .../CustomFormatSpecificationBase.cs | 6 +- .../Specifications/EditionSpecification.cs | 6 +- .../ICustomFormatSpecification.cs | 2 +- .../IndexerFlagSpecification.cs | 5 +- .../Specifications/LanguageSpecification.cs | 8 +- .../QualityModifierSpecification.cs | 4 +- .../ReleaseGroupSpecification.cs | 4 +- .../ReleaseTitleSpecification.cs | 6 +- .../Specifications/ResolutionSpecification.cs | 4 +- .../Specifications/SizeSpecification.cs | 4 +- .../Specifications/SourceSpecification.cs | 4 +- .../DecisionEngine/DownloadDecisionMaker.cs | 40 +--- .../HardcodeSubsSpecification.cs | 47 ++++ .../Specifications/QueueSpecification.cs | 2 +- .../RssSync/HistorySpecification.cs | 2 +- .../Aggregators/AggregateLanguages.cs | 86 +++++++ .../Aggregators/IAggregateRemoteMovie.cs | 9 + .../RemoteMovieAggregationService.cs | 43 ++++ .../Download/Clients/Flood/Flood.cs | 2 +- .../Download/IgnoredDownloadService.cs | 2 +- .../Download/Pending/PendingReleaseService.cs | 11 +- .../TrackedDownloadService.cs | 10 +- src/NzbDrone.Core/History/HistoryService.cs | 3 +- .../MovieImport/ImportDecisionMaker.cs | 13 +- .../MovieImport/Manual/ManualImportItem.cs | 7 + .../MovieImport/Manual/ManualImportService.cs | 9 + .../CustomScript/CustomScript.cs | 2 + .../Notifications/DownloadMessage.cs | 2 + .../Notifications/NotificationService.cs | 1 + .../Notifications/Webhook/WebhookBase.cs | 6 +- .../Webhook/WebhookCustomFormat.cs | 18 ++ .../Webhook/WebhookCustomFormatInfo.cs | 18 ++ .../Webhook/WebhookGrabPayload.cs | 1 + .../Webhook/WebhookImportPayload.cs | 1 + .../Organizer/FileNameBuilder.cs | 8 +- .../Parser/Augmenters/AugmentWithFileSize.cs | 26 --- .../Parser/Augmenters/AugmentWithHistory.cs | 59 ----- .../Parser/Augmenters/AugmentWithMediaInfo.cs | 58 ----- .../Augmenters/AugmentWithOriginalLanguage.cs | 27 --- .../Augmenters/AugmentWithParsedMovieInfo.cs | 42 ---- .../Augmenters/AugmentWithReleaseInfo.cs | 69 ------ .../Augmenters/IAugmentParsedMovieInfo.cs | 12 - src/NzbDrone.Core/Parser/Model/LocalMovie.cs | 4 + .../Parser/Model/ParsedMovieInfo.cs | 3 +- src/NzbDrone.Core/Parser/Model/RemoteMovie.cs | 8 + src/NzbDrone.Core/Parser/Parser.cs | 24 ++ src/NzbDrone.Core/Parser/ParsingService.cs | 44 ---- src/NzbDrone.Core/Parser/QualityParser.cs | 16 -- src/NzbDrone.Core/Qualities/QualityModel.cs | 2 - .../Blocklist/BlocklistController.cs | 4 +- .../Blocklist/BlocklistResource.cs | 2 +- .../CustomFormats/CustomFormatController.cs | 7 +- .../CustomFormats/CustomFormatResource.cs | 30 ++- src/Radarr.Api.V3/History/HistoryResource.cs | 7 +- src/Radarr.Api.V3/Indexers/ReleaseResource.cs | 4 +- .../ManualImport/ManualImportResource.cs | 5 +- .../MovieFiles/MovieFileController.cs | 4 +- src/Radarr.Api.V3/Parse/ParseController.cs | 9 +- src/Radarr.Api.V3/Queue/QueueResource.cs | 2 +- 90 files changed, 767 insertions(+), 1053 deletions(-) rename src/NzbDrone.Core.Test/CustomFormats/{CustomFormatsFixture.cs => CustomFormatsTestHelpers.cs} (94%) create mode 100644 src/NzbDrone.Core.Test/Download/Aggregation/Aggregators/AggregateLanguagesFixture.cs delete mode 100644 src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentMovieInfoFixture.cs delete mode 100644 src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithFileSizeFixture.cs delete mode 100644 src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithHistoryFixture.cs delete mode 100644 src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithMediaInfoFixture.cs delete mode 100644 src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithOriginalLanguageFixture.cs delete mode 100644 src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithParsedMovieInfo.cs delete mode 100644 src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithReleaseInfoFixture.cs create mode 100644 src/NzbDrone.Core/CustomFormats/CustomFormatInput.cs create mode 100644 src/NzbDrone.Core/DecisionEngine/Specifications/HardcodeSubsSpecification.cs create mode 100644 src/NzbDrone.Core/Download/Aggregation/Aggregators/AggregateLanguages.cs create mode 100644 src/NzbDrone.Core/Download/Aggregation/Aggregators/IAggregateRemoteMovie.cs create mode 100644 src/NzbDrone.Core/Download/Aggregation/RemoteMovieAggregationService.cs create mode 100644 src/NzbDrone.Core/Notifications/Webhook/WebhookCustomFormat.cs create mode 100644 src/NzbDrone.Core/Notifications/Webhook/WebhookCustomFormatInfo.cs delete mode 100644 src/NzbDrone.Core/Parser/Augmenters/AugmentWithFileSize.cs delete mode 100644 src/NzbDrone.Core/Parser/Augmenters/AugmentWithHistory.cs delete mode 100644 src/NzbDrone.Core/Parser/Augmenters/AugmentWithMediaInfo.cs delete mode 100644 src/NzbDrone.Core/Parser/Augmenters/AugmentWithOriginalLanguage.cs delete mode 100644 src/NzbDrone.Core/Parser/Augmenters/AugmentWithParsedMovieInfo.cs delete mode 100644 src/NzbDrone.Core/Parser/Augmenters/AugmentWithReleaseInfo.cs delete mode 100644 src/NzbDrone.Core/Parser/Augmenters/IAugmentParsedMovieInfo.cs diff --git a/frontend/src/Activity/History/HistoryRow.js b/frontend/src/Activity/History/HistoryRow.js index 9a5b4d283..d294b1ec4 100644 --- a/frontend/src/Activity/History/HistoryRow.js +++ b/frontend/src/Activity/History/HistoryRow.js @@ -56,6 +56,7 @@ class HistoryRow extends Component { movie, quality, customFormats, + customFormatScore, languages, qualityCutoffNotMet, eventType, @@ -175,7 +176,7 @@ class HistoryRow extends Component { key={name} className={styles.customFormatScore} > - {formatCustomFormatScore(data.customFormatScore)} + {formatCustomFormatScore(customFormatScore)} ); } @@ -241,8 +242,9 @@ HistoryRow.propTypes = { movie: PropTypes.object.isRequired, languages: PropTypes.arrayOf(PropTypes.object).isRequired, quality: PropTypes.object.isRequired, + customFormats: PropTypes.arrayOf(PropTypes.object), + customFormatScore: PropTypes.number.isRequired, qualityCutoffNotMet: PropTypes.bool.isRequired, - customFormats: PropTypes.arrayOf(PropTypes.object).isRequired, eventType: PropTypes.string.isRequired, sourceTitle: PropTypes.string.isRequired, date: PropTypes.string.isRequired, diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js index 7fb42308d..2eb90c759 100644 --- a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js +++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js @@ -64,6 +64,15 @@ const columns = [ isSortable: true, isVisible: true }, + { + name: 'customFormats', + label: React.createElement(Icon, { + name: icons.INTERACTIVE, + title: translate('CustomFormat') + }), + isSortable: true, + isVisible: true + }, { name: 'rejections', label: React.createElement(Icon, { diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css index f1b7b44e7..d3cfb118a 100644 --- a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css +++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css @@ -5,8 +5,10 @@ } .quality, -.language { +.languages { composes: cell from '~Components/Table/Cells/TableRowCell.css'; + + text-align: center; } .label { @@ -21,3 +23,7 @@ margin-top: 0; text-align: start; } + +.customFormatTooltip { + max-width: 250px; +} diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js index f21956fdf..e5cdc55dc 100644 --- a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js +++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js @@ -12,6 +12,7 @@ import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal' import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal'; import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal'; import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal'; +import MovieFormats from 'Movie/MovieFormats'; import MovieLanguage from 'Movie/MovieLanguage'; import MovieQuality from 'Movie/MovieQuality'; import formatBytes from 'Utilities/Number/formatBytes'; @@ -150,6 +151,7 @@ class InteractiveImportRow extends Component { languages, releaseGroup, size, + customFormats, rejections, isReprocessing, isSelected, @@ -226,7 +228,7 @@ class InteractiveImportRow extends Component { @@ -259,7 +261,26 @@ class InteractiveImportRow extends Component { { - !!rejections.length && + customFormats?.length ? + + } + title="Formats" + body={ +
+ +
+ } + position={tooltipPositions.LEFT} + /> : + null + } +
+ + + { + rejections.length ? } position={tooltipPositions.LEFT} - /> + canFlip={false} + /> : + null } @@ -330,6 +353,7 @@ InteractiveImportRow.propTypes = { languages: PropTypes.arrayOf(PropTypes.object), releaseGroup: PropTypes.string, size: PropTypes.number.isRequired, + customFormats: PropTypes.arrayOf(PropTypes.object), rejections: PropTypes.arrayOf(PropTypes.object).isRequired, isReprocessing: PropTypes.bool, isSelected: PropTypes.bool, diff --git a/frontend/src/Movie/History/MovieHistoryRow.js b/frontend/src/Movie/History/MovieHistoryRow.js index 406c5ee4f..9e2800449 100644 --- a/frontend/src/Movie/History/MovieHistoryRow.js +++ b/frontend/src/Movie/History/MovieHistoryRow.js @@ -62,6 +62,7 @@ class MovieHistoryRow extends Component { sourceTitle, quality, customFormats, + customFormatScore, languages, qualityCutoffNotMet, date, @@ -106,7 +107,7 @@ class MovieHistoryRow extends Component { - {formatCustomFormatScore(data.customFormatScore)} + {formatCustomFormatScore(customFormatScore)} 0) { @@ -10,7 +9,7 @@ function formatCustomFormatScore(input) { return score; } - return ''; + return customFormatsLength > 0 ? '+0' : ''; } export default formatCustomFormatScore; diff --git a/src/NzbDrone.Core.Test/CustomFormats/CustomFormatsFixture.cs b/src/NzbDrone.Core.Test/CustomFormats/CustomFormatsTestHelpers.cs similarity index 94% rename from src/NzbDrone.Core.Test/CustomFormats/CustomFormatsFixture.cs rename to src/NzbDrone.Core.Test/CustomFormats/CustomFormatsTestHelpers.cs index bd1e8a815..e2f94f3e6 100644 --- a/src/NzbDrone.Core.Test/CustomFormats/CustomFormatsFixture.cs +++ b/src/NzbDrone.Core.Test/CustomFormats/CustomFormatsTestHelpers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -9,7 +9,7 @@ using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.CustomFormats { [TestFixture] - public class CustomFormatsFixture : CoreTest + public class CustomFormatsTestHelpers : CoreTest { private static List _customFormats { get; set; } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs index a61d5281f..8f7c7718a 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs @@ -46,14 +46,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, }; - CustomFormatsFixture.GivenCustomFormats(_format1, _format2); + CustomFormatsTestHelpers.GivenCustomFormats(_format1, _format2); } [Test] public void should_allow_if_format_score_greater_than_min() { _remoteMovie.CustomFormats = new List { _format1 }; - _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); + _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_deny_if_format_score_not_greater_than_min() { _remoteMovie.CustomFormats = new List { _format2 }; - _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); + _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); Console.WriteLine(_remoteMovie.CustomFormatScore); @@ -76,7 +76,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_deny_if_format_score_not_greater_than_min_2() { _remoteMovie.CustomFormats = new List { _format2, _format1 }; - _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); + _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse(); @@ -86,7 +86,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_allow_if_all_format_is_defined_in_profile() { _remoteMovie.CustomFormats = new List { _format2, _format1 }; - _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); + _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); @@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_deny_if_no_format_was_parsed_and_min_score_positive() { _remoteMovie.CustomFormats = new List { }; - _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); + _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse(); @@ -106,7 +106,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_allow_if_no_format_was_parsed_min_score_is_zero() { _remoteMovie.CustomFormats = new List { }; - _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); + _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name); _remoteMovie.Movie.Profile.MinFormatScore = 0; _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs index 7f1ec040b..8cf7f5cd7 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs @@ -40,8 +40,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests private void GivenProfile(Profile profile) { - CustomFormatsFixture.GivenCustomFormats(); - profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(); + CustomFormatsTestHelpers.GivenCustomFormats(); + profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(); profile.MinFormatScore = 0; _remoteMovie.Movie.Profile = profile; @@ -74,7 +74,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { _customFormat = new CustomFormat("My Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 1 }; - CustomFormatsFixture.GivenCustomFormats(_customFormat); + CustomFormatsTestHelpers.GivenCustomFormats(_customFormat); } [Test] @@ -157,7 +157,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Cutoff = Quality.HDTV720p.Id, Items = Qualities.QualityFixture.GetDefaultQualities(), MinFormatScore = 0, - FormatItems = CustomFormatsFixture.GetSampleFormatItems("My Format"), + FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("My Format"), UpgradeAllowed = true }); diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs index 0e3fbbdf8..46db26684 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs @@ -38,14 +38,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Mocker.Resolve(); _upgradeHistory = Mocker.Resolve(); - CustomFormatsFixture.GivenCustomFormats(); + CustomFormatsTestHelpers.GivenCustomFormats(); _fakeMovie = Builder.CreateNew() .With(c => c.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities(), Cutoff = Quality.Bluray1080p.Id, - FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"), + FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("None"), MinFormatScore = 0, UpgradeAllowed = true }) @@ -66,7 +66,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests .Returns(true); Mocker.GetMock() - .Setup(x => x.ParseCustomFormat(It.IsAny())) + .Setup(x => x.ParseCustomFormat(It.IsAny(), It.IsAny())) .Returns(new List()); } @@ -163,7 +163,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { Items = Qualities.QualityFixture.GetDefaultQualities(), Cutoff = Quality.Bluray1080p.Id, - FormatItems = CustomFormatsFixture.GetSampleFormatItems(), + FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), MinFormatScore = 0 }; @@ -171,7 +171,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests _upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); Mocker.GetMock() - .Setup(x => x.ParseCustomFormat(It.IsAny())) + .Setup(x => x.ParseCustomFormat(It.IsAny(), It.IsAny())) .Returns(new List()); GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, MovieHistoryEventType.Grabbed); @@ -186,7 +186,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { Items = Qualities.QualityFixture.GetDefaultQualities(), Cutoff = Quality.WEBDL1080p.Id, - FormatItems = CustomFormatsFixture.GetSampleFormatItems(), + FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), MinFormatScore = 0 }; @@ -221,7 +221,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { Items = Qualities.QualityFixture.GetDefaultQualities(), Cutoff = Quality.WEBDL1080p.Id, - FormatItems = CustomFormatsFixture.GetSampleFormatItems(), + FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), MinFormatScore = 0 }; diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs index 7135778d6..4afb8ccaf 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs @@ -37,7 +37,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests _customFormat1 = new CustomFormat("My Format 1", new LanguageSpecification { Value = (int)Language.English }) { Id = 1 }; _customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 }; - CustomFormatsFixture.GivenCustomFormats(_customFormat1, _customFormat2); + CustomFormatsTestHelpers.GivenCustomFormats(_customFormat1, _customFormat2); Mocker.GetMock() .Setup(s => s.Get(It.IsAny())) @@ -62,7 +62,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests remoteMovie.Movie = Builder.CreateNew().With(m => m.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities(), - FormatItems = CustomFormatsFixture.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name), + FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name), MinFormatScore = 0 }) .With(m => m.Title = "A Movie") diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs index 75b8894b7..290bda7ce 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs @@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [SetUp] public void Setup() { - CustomFormatsFixture.GivenCustomFormats(_customFormat1, _customFormat2); + CustomFormatsTestHelpers.GivenCustomFormats(_customFormat1, _customFormat2); } private void GivenAutoDownloadPropers(ProperDownloadTypes type) @@ -73,7 +73,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities(), - FormatItems = CustomFormatsFixture.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name), + FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name), MinFormatScore = 0 }; diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs index 537413b6d..8f17e1179 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs @@ -32,13 +32,13 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { Mocker.Resolve(); - CustomFormatsFixture.GivenCustomFormats(); + CustomFormatsTestHelpers.GivenCustomFormats(); _movie = Builder.CreateNew() .With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities(), - FormatItems = CustomFormatsFixture.GetSampleFormatItems(), + FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), MinFormatScore = 0, UpgradeAllowed = true }) @@ -58,7 +58,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests .Build(); Mocker.GetMock() - .Setup(x => x.ParseCustomFormat(It.IsAny(), _movie)) + .Setup(x => x.ParseCustomFormat(It.IsAny(), It.IsAny())) .Returns(new List()); } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs index 6ea7e9d14..33f103a20 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Mocker.Resolve(); _upgradeDisk = Mocker.Resolve(); - CustomFormatsFixture.GivenCustomFormats(); + CustomFormatsTestHelpers.GivenCustomFormats(); _firstFile = new MovieFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now }; @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities(), - FormatItems = CustomFormatsFixture.GetSampleFormatItems(), + FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), MinFormatScore = 0 }) .With(e => e.MovieFile = _firstFile) diff --git a/src/NzbDrone.Core.Test/Download/Aggregation/Aggregators/AggregateLanguagesFixture.cs b/src/NzbDrone.Core.Test/Download/Aggregation/Aggregators/AggregateLanguagesFixture.cs new file mode 100644 index 000000000..6fa686164 --- /dev/null +++ b/src/NzbDrone.Core.Test/Download/Aggregation/Aggregators/AggregateLanguagesFixture.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Download.Aggregation.Aggregators; +using NzbDrone.Core.Languages; +using NzbDrone.Core.Movies; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.Download.Aggregation.Aggregators +{ + [TestFixture] + public class AggregateLanguagesFixture : CoreTest + { + private RemoteMovie _remoteMovie; + private Movie _movie; + private string _simpleReleaseTitle = "Series.Title.S01E01.xyz-RlsGroup"; + + [SetUp] + public void Setup() + { + _movie = Builder.CreateNew() + .With(m => m.MovieMetadata = new MovieMetadata + { + Title = "Some Movie", + OriginalLanguage = Language.English + }) + .Build(); + + _remoteMovie = Builder.CreateNew() + .With(l => l.ParsedMovieInfo = null) + .With(l => l.Movie = _movie) + .Build(); + } + + private ParsedMovieInfo GetParsedMovieInfo(List languages, string releaseTitle, string releaseTokens = "") + { + return new ParsedMovieInfo + { + Languages = languages, + ReleaseTitle = releaseTitle, + SimpleReleaseTitle = releaseTokens + }; + } + + [Test] + public void should_return_existing_language_if_episode_title_does_not_have_language() + { + _remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List { Language.Original }, _simpleReleaseTitle); + + Subject.Aggregate(_remoteMovie).Languages.Should().Contain(_movie.MovieMetadata.Value.OriginalLanguage); + } + + [Test] + public void should_return_parsed_language() + { + _remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List { Language.French }, _simpleReleaseTitle); + + Subject.Aggregate(_remoteMovie).Languages.Should().Equal(_remoteMovie.ParsedMovieInfo.Languages); + } + + [Test] + public void should_exclude_language_that_is_part_of_episode_title_when_release_tokens_contains_episode_title() + { + var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.xyz-RlsGroup"; + var releaseTokens = ".Jimmy.The.Greek.xyz-RlsGroup"; + + _remoteMovie.Movie.Title = "Jimmy The Greek"; + _remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List { Language.Greek }, releaseTitle, releaseTokens); + + Subject.Aggregate(_remoteMovie).Languages.Should().Equal(_movie.MovieMetadata.Value.OriginalLanguage); + } + + [Test] + public void should_remove_parsed_language_that_is_part_of_episode_title_when_release_tokens_contains_episode_title() + { + var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.French.xyz-RlsGroup"; + var releaseTokens = ".Jimmy.The.Greek.French.xyz-RlsGroup"; + + _remoteMovie.Movie.Title = "Jimmy The Greek"; + _remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List { Language.Greek, Language.French }, releaseTitle, releaseTokens); + + Subject.Aggregate(_remoteMovie).Languages.Should().Equal(Language.French); + } + + [Test] + public void should_not_exclude_language_that_is_part_of_episode_title_when_release_tokens_does_not_contain_episode_title() + { + var releaseTitle = "Series.Title.S01E01.xyz-RlsGroup"; + var releaseTokens = ".xyz-RlsGroup"; + + _remoteMovie.Movie.Title = "Jimmy The Greek"; + _remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List { Language.Greek }, releaseTitle, releaseTokens); + + Subject.Aggregate(_remoteMovie).Languages.Should().Equal(Language.Greek); + } + + [Test] + public void should_use_reparse_language_after_determining_languages_that_are_in_episode_titles() + { + var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.Greek.xyz-RlsGroup"; + var releaseTokens = ".Jimmy.The.Greek.Greek.xyz-RlsGroup"; + + _remoteMovie.Movie.Title = "Jimmy The Greek"; + _remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List { Language.Greek }, releaseTitle, releaseTokens); + + Subject.Aggregate(_remoteMovie).Languages.Should().Equal(Language.Greek); + } + } +} diff --git a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs index 32010c6a5..b780a277a 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemovePendingFixture.cs @@ -54,6 +54,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests Id = id, Title = "Movie.Title.2020.720p-Radarr", ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List { title }, Year = year }, + Release = Builder.CreateNew().Build(), MovieId = _movie.Id }); } diff --git a/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs b/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs index f0c4cde94..65a2b015b 100644 --- a/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs @@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p), - FormatItems = CustomFormatsFixture.GetDefaultFormatItems(), + FormatItems = CustomFormatsTestHelpers.GetDefaultFormatItems(), MinFormatScore = 0, Cutoff = Quality.Bluray1080p.Id, Name = "TestProfile" diff --git a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index a8038f4e7..78933e8e7 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -255,6 +255,18 @@ namespace NzbDrone.Core.Test.ParserTests parsed.Languages.Should().Contain(Language.German); } + [TestCase("Movie.Title.2016.1080p.KORSUB.WEBRip.x264.AAC2.0-RADARR", "KORSUB")] + [TestCase("Movie.Title.2016.1080p.KORSUBS.WEBRip.x264.AAC2.0-RADARR", "KORSUBS")] + [TestCase("Movie Title 2017 HC 720p HDRiP DD5 1 x264-LEGi0N", "Generic Hardcoded Subs")] + [TestCase("Movie.Title.2017.720p.SUBBED.HDRip.V2.XViD-26k.avi", "Generic Hardcoded Subs")] + [TestCase("Movie.Title.2000.1080p.BlueRay.x264.DTS.RoSubbed-playHD", null)] + [TestCase("Movie Title! 2018 [Web][MKV][h264][480p][AAC 2.0][Softsubs]", null)] + [TestCase("Movie Title! 2019 [HorribleSubs][Web][MKV][h264][848x480][AAC 2.0][Softsubs(HorribleSubs)]", null)] + public void should_parse_hardcoded_subs(string postTitle, string sub) + { + Parser.Parser.ParseMovieTitle(postTitle).HardcodedSubs.Should().Be(sub); + } + [TestCase("That Italian Movie 2008 [tt1234567] 720p BluRay X264", "tt1234567")] [TestCase("That Italian Movie 2008 [tt12345678] 720p BluRay X264", "tt12345678")] public void should_parse_imdb_in_title(string postTitle, string imdb) diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentMovieInfoFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentMovieInfoFixture.cs deleted file mode 100644 index 20a862d9b..000000000 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentMovieInfoFixture.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using NUnit.Framework; -using NzbDrone.Core.Parser.Augmenters; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests -{ - [TestFixture] - public abstract class AugmentMovieInfoFixture : CoreTest - where TAugmenter : class, IAugmentParsedMovieInfo - { - protected ParsedMovieInfo MovieInfo; - - [SetUp] - public virtual void Setup() - { - MovieInfo = new ParsedMovieInfo - { - MovieTitles = new List { "A Movie" }, - Year = 1998, - SimpleReleaseTitle = "A Movie Title 1998 Bluray 1080p", - Quality = new QualityModel(Quality.Bluray1080p) - }; - } - } -} diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithFileSizeFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithFileSizeFixture.cs deleted file mode 100644 index e36441253..000000000 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithFileSizeFixture.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Parser.Augmenters; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests -{ - [TestFixture] - public class AugmentWithFileSizeFixture : AugmentMovieInfoFixture - { - [Test] - public void should_add_file_size() - { - var localMovie = new LocalMovie - { - Size = 1500 - }; - - var movieInfo = Subject.AugmentMovieInfo(MovieInfo, localMovie); - movieInfo.ExtraInfo["Size"].Should().BeEquivalentTo(1500); - } - } -} diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithHistoryFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithHistoryFixture.cs deleted file mode 100644 index 498ef7a37..000000000 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithHistoryFixture.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.History; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Indexers.Rarbg; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Parser.Augmenters; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests -{ - [TestFixture] - public class AugmentWithHistoryFixture : AugmentMovieInfoFixture - { - private AugmentWithHistory _customSubject { get; set; } - - [SetUp] - public override void Setup() - { - base.Setup(); - - // Add multi indexer - GivenIndexerSettings(new RarbgSettings - { - MultiLanguages = new List - { - (int)Language.English, - (int)Language.French, - } - }); - } - - protected new AugmentWithHistory Subject - { - get - { - if (_customSubject == null) - { - _customSubject = new AugmentWithHistory(new List> { new (Mocker.Resolve()) }); - } - - return _customSubject; - } - } - - private void GivenIndexerSettings(IIndexerSettings indexerSettings) - { - Mocker.GetMock().Setup(f => f.Get(It.IsAny())).Returns(new IndexerDefinition - { - Settings = indexerSettings - }); - } - - private MovieHistory HistoryWithData(params string[] data) - { - var dict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - for (var i = 0; i < data.Length; i += 2) - { - dict.Add(data[i], data[i + 1]); - } - - return new MovieHistory - { - Data = dict, - EventType = MovieHistoryEventType.Grabbed - }; - } - - [Test] - public void should_add_indexer_flags() - { - var history = HistoryWithData("IndexerFlags", (IndexerFlags.PTP_Approved | IndexerFlags.PTP_Golden).ToString()); - var movieInfo = Subject.AugmentMovieInfo(MovieInfo, history); - movieInfo.ExtraInfo["IndexerFlags"].Should().BeEquivalentTo(IndexerFlags.PTP_Golden | IndexerFlags.PTP_Approved); - } - - [Test] - public void should_add_size() - { - var history = HistoryWithData("Size", 9663676416.ToString()); - var movieInfo = Subject.AugmentMovieInfo(MovieInfo, history); - movieInfo.ExtraInfo["Size"].Should().BeEquivalentTo(9663676416); - } - - [Test] - public void should_use_settings_languages_when_necessary() - { - var history = HistoryWithData("IndexerId", 1.ToString()); - - var movieInfo = Subject.AugmentMovieInfo(MovieInfo, history); - movieInfo.Languages.Should().BeEquivalentTo(); - - MovieInfo.SimpleReleaseTitle = "A Movie 1998 Bluray 1080p MULTI"; - var multiInfo = Subject.AugmentMovieInfo(MovieInfo, history); - multiInfo.Languages.Should().BeEquivalentTo(Language.English, Language.French); - } - - [Test] - public void should_not_use_settings_languages() - { - var unknownIndexer = HistoryWithData(); - var unknownIndexerInfo = Subject.AugmentMovieInfo(MovieInfo, unknownIndexer); - unknownIndexerInfo.Languages.Should().BeEquivalentTo(); - } - } -} diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithMediaInfoFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithMediaInfoFixture.cs deleted file mode 100644 index 19451afe4..000000000 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithMediaInfoFixture.cs +++ /dev/null @@ -1,90 +0,0 @@ -// using FluentAssertions; -// using NUnit.Framework; -// using NzbDrone.Core.CustomFormats; -// using NzbDrone.Core.MediaFiles.MediaInfo; -// using NzbDrone.Core.Parser; -// using NzbDrone.Core.Parser.Augmenters; -// using NzbDrone.Core.Qualities; - -// namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests -// { -// [TestFixture] -// public class AugmentWithMediaInfoFixture : AugmentMovieInfoFixture -// { -// [TestCase(Resolution.R720p, Source.BLURAY, Resolution.R1080p)] -// [TestCase(Resolution.R1080p, Source.TV, Resolution.R720p)] -// public void should_correct_resolution(Resolution resolution, Source source, Resolution realResolution) -// { -// var quality = new QualityModel -// { -// Source = source, -// Resolution = resolution, -// }; -// MovieInfo.Quality = quality; - -// var realWidth = 480; -// switch (realResolution) -// { -// case Resolution.R720p: -// realWidth = 1280; -// break; -// case Resolution.R1080p: -// realWidth = 1920; -// break; -// case Resolution.R2160p: -// realWidth = 2160; -// break; - -// } - -// var mediaInfo = new MediaInfoModel -// { -// Width = realWidth -// }; - -// var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo); -// movieInfo.Quality.Resolution.Should().BeEquivalentTo(realResolution); -// movieInfo.Quality.QualityDetectionSource.Should().BeEquivalentTo(QualityDetectionSource.MediaInfo); -// } - -// [TestCase(Resolution.R720P, Source.BLURAY, Resolution.R1080P, Modifier.BRDISK)] -// [TestCase(Resolution.R1080P, Source.BLURAY, Resolution.R720P, Modifier.REMUX)] -// [TestCase(Resolution.R480P, Source.BLURAY, Resolution.R720P)] -// [TestCase(Resolution.R720P, Source.DVD, Resolution.R480P)] -// public void should_not_correct_resolution(Resolution resolution, Source source, Resolution realResolution, Modifier modifier = Modifier.NONE) -// { -// var quality = new QualityModel -// { -// Source = source, -// Resolution = resolution, -// Modifier = modifier, -// }; - -// MovieInfo.Quality = quality; - -// var realWidth = 480; -// switch (realResolution) -// { -// case Resolution.R720P: -// realWidth = 1280; -// break; -// case Resolution.R1080P: -// realWidth = 1920; -// break; -// case Resolution.R2160P: -// realWidth = 2160; -// break; - -// } - -// var mediaInfo = new MediaInfoModel -// { -// Width = realWidth -// }; - -// var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo); -// movieInfo.Quality.Resolution.Should().BeEquivalentTo(resolution); -// movieInfo.Quality.QualityDetectionSource.Should().BeEquivalentTo(QualityDetectionSource.Name); -// } -// } -// } diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithOriginalLanguageFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithOriginalLanguageFixture.cs deleted file mode 100644 index 7a149db9a..000000000 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithOriginalLanguageFixture.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Parser.Augmenters; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests -{ - [TestFixture] - public class AugmentWithOriginalLanguageFixture : AugmentMovieInfoFixture - { - [Test] - public void should_add_movie_original_language() - { - var releaseInfo = new ParsedMovieInfo(); - var movie = new Movies.Movie - { - MovieMetadata = new Movies.MovieMetadata - { - OriginalLanguage = Language.English - } - }; - var result = Subject.AugmentMovieInfo(releaseInfo, movie); - result.ExtraInfo.Should().ContainKey("OriginalLanguage"); - result.ExtraInfo["OriginalLanguage"].Should().Be(Language.English); - } - } -} diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithParsedMovieInfo.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithParsedMovieInfo.cs deleted file mode 100644 index d428345da..000000000 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithParsedMovieInfo.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Collections.Generic; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Parser.Augmenters; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests -{ - [TestFixture] - public class AugmentWithParsedMovieInfoFixture : AugmentMovieInfoFixture - { - [Test] - public void should_add_edition_if_null() - { - var folderInfo = new ParsedMovieInfo - { - Edition = "Directors Cut" - }; - - var result = Subject.AugmentMovieInfo(MovieInfo, folderInfo); - - result.Edition.Should().Be(folderInfo.Edition); - } - - [Test] - public void should_preferr_longer_edition() - { - var folderInfo = new ParsedMovieInfo - { - Edition = "Super duper cut" - }; - - MovieInfo.Edition = "Rogue"; - - var result = Subject.AugmentMovieInfo(MovieInfo, folderInfo); - - result.Edition.Should().Be(folderInfo.Edition); - - MovieInfo.Edition = "Super duper awesome cut"; - - result = Subject.AugmentMovieInfo(MovieInfo, folderInfo); - - result.Edition.Should().Be(MovieInfo.Edition); - } - - [Test] - public void should_combine_languages() - { - var folderInfo = new ParsedMovieInfo - { - Languages = new List { Language.French } - }; - - MovieInfo.Languages = new List { Language.English }; - - var result = Subject.AugmentMovieInfo(MovieInfo, folderInfo); - - result.Languages.Should().BeEquivalentTo(Language.English, Language.French); - } - - [Test] - public void should_use_folder_release_group() - { - var folderInfo = new ParsedMovieInfo - { - ReleaseGroup = "AwesomeGroup" - }; - - MovieInfo.ReleaseGroup = ""; - - var result = Subject.AugmentMovieInfo(MovieInfo, folderInfo); - - result.ReleaseGroup.Should().BeEquivalentTo(folderInfo.ReleaseGroup); - } - } -} diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithReleaseInfoFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithReleaseInfoFixture.cs deleted file mode 100644 index e4e8acd8e..000000000 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/AugmentersTests/AugmentWithReleaseInfoFixture.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Linq; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Indexers.Rarbg; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Parser.Augmenters; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests -{ - [TestFixture] - public class AugmentWithReleaseInfoFixture : AugmentMovieInfoFixture - { - private IndexerDefinition _indexerDefinition; - - private ReleaseInfo ReleaseInfoWithLanguages(params Language[] languages) - { - _indexerDefinition = new IndexerDefinition - { - Settings = new RarbgSettings { MultiLanguages = languages.ToList().Select(l => (int)l) } - }; - - Mocker.GetMock() - .Setup(v => v.Get(1)) - .Returns(_indexerDefinition); - - return new ReleaseInfo - { - IndexerId = 1 - }; - } - - [Test] - public void should_add_language_from_indexer() - { - var releaseInfo = ReleaseInfoWithLanguages(Language.English, Language.French); - MovieInfo.SimpleReleaseTitle = "A Movie Title 1998 Bluray 1080p MULTI"; - var movieInfo = Subject.AugmentMovieInfo(MovieInfo, releaseInfo); - movieInfo.Languages.Count.Should().Be(2); - movieInfo.Languages.Should().BeEquivalentTo(Language.English, Language.French); - } - - [Test] - public void should_add_size_info() - { - var releaseInfo = new ReleaseInfo - { - Size = 1500 - }; - - var movieInfo = Subject.AugmentMovieInfo(MovieInfo, releaseInfo); - movieInfo.ExtraInfo["Size"].Should().BeEquivalentTo(1500); - } - - [Test] - public void should_not_add_size_when_already_present() - { - var releaseInfo = new ReleaseInfo - { - Size = 1500 - }; - - MovieInfo.ExtraInfo["Size"] = 1600; - - var movieInfo = Subject.AugmentMovieInfo(MovieInfo, releaseInfo); - movieInfo.ExtraInfo["Size"].Should().BeEquivalentTo(1600); - } - - [Test] - public void should_add_indexer_flags() - { - var releaseInfo = new ReleaseInfo - { - IndexerFlags = IndexerFlags.PTP_Approved | IndexerFlags.PTP_Golden - }; - - var movieInfo = Subject.AugmentMovieInfo(MovieInfo, releaseInfo); - movieInfo.ExtraInfo["IndexerFlags"].Should().BeEquivalentTo(IndexerFlags.PTP_Approved | IndexerFlags.PTP_Golden); - } - } -} diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs index eed7a7ab7..080e33959 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs @@ -194,20 +194,5 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests Subject.Map(_umlautInfo, "", _movieSearchCriteria).Movie.Should().Be(_movieSearchCriteria.Movie); Subject.Map(_umlautAltInfo, "", _movieSearchCriteria).Movie.Should().Be(_movieSearchCriteria.Movie); } - - [Test] - public void should_convert_original() - { - Subject.Map(_multiLanguageInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().Contain(Language.English); - Subject.Map(_multiLanguageInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().Contain(Language.French); - } - - [Test] - public void should_remove_original_as_already_exists() - { - Subject.Map(_multiLanguageWithOriginalInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().Contain(Language.English); - Subject.Map(_multiLanguageWithOriginalInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().Contain(Language.French); - Subject.Map(_multiLanguageWithOriginalInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().NotContain(Language.Original); - } } } diff --git a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs index 7d36bd8e8..9371c68df 100644 --- a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs @@ -452,18 +452,6 @@ namespace NzbDrone.Core.Test.ParserTests result.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name); } - [TestCase("Movie.Title.2016.1080p.KORSUB.WEBRip.x264.AAC2.0-RADARR", "KORSUB")] - [TestCase("Movie.Title.2016.1080p.KORSUBS.WEBRip.x264.AAC2.0-RADARR", "KORSUBS")] - [TestCase("Movie Title 2017 HC 720p HDRiP DD5 1 x264-LEGi0N", "Generic Hardcoded Subs")] - [TestCase("Movie.Title.2017.720p.SUBBED.HDRip.V2.XViD-26k.avi", "Generic Hardcoded Subs")] - [TestCase("Movie.Title.2000.1080p.BlueRay.x264.DTS.RoSubbed-playHD", null)] - [TestCase("Movie Title! 2018 [Web][MKV][h264][480p][AAC 2.0][Softsubs]", null)] - [TestCase("Movie Title! 2019 [HorribleSubs][Web][MKV][h264][848x480][AAC 2.0][Softsubs(HorribleSubs)]", null)] - public void should_parse_hardcoded_subs(string postTitle, string sub) - { - QualityParser.ParseQuality(postTitle).HardcodedSubs.Should().Be(sub); - } - [TestCase("Movie Title 2018 REPACK 720p x264 aAF", true)] [TestCase("Movie.Title.2018.REPACK.720p.x264-aAF", true)] [TestCase("Movie.Title.2018.PROPER.720p.x264-aAF", false)] diff --git a/src/NzbDrone.Core.Test/Profiles/ProfileRepositoryFixture.cs b/src/NzbDrone.Core.Test/Profiles/ProfileRepositoryFixture.cs index e8737c404..d3e16ec57 100644 --- a/src/NzbDrone.Core.Test/Profiles/ProfileRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/Profiles/ProfileRepositoryFixture.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.Profiles { Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p), MinFormatScore = 0, - FormatItems = CustomFormatsFixture.GetDefaultFormatItems(), + FormatItems = CustomFormatsTestHelpers.GetDefaultFormatItems(), Cutoff = Quality.Bluray1080p.Id, Name = "TestProfile" }; diff --git a/src/NzbDrone.Core.Test/Profiles/ProfileServiceFixture.cs b/src/NzbDrone.Core.Test/Profiles/ProfileServiceFixture.cs index b35386eb6..033c435e4 100644 --- a/src/NzbDrone.Core.Test/Profiles/ProfileServiceFixture.cs +++ b/src/NzbDrone.Core.Test/Profiles/ProfileServiceFixture.cs @@ -170,9 +170,9 @@ namespace NzbDrone.Core.Test.Profiles var customFormat1 = new CustomFormat("My Format 1", new LanguageSpecification { Value = (int)Language.English }) { Id = 1 }; var customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 }; - CustomFormatsFixture.GivenCustomFormats(customFormat1, customFormat2); + CustomFormatsTestHelpers.GivenCustomFormats(customFormat1, customFormat2); - profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(customFormat2.Name); + profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(customFormat2.Name); Mocker.GetMock() .Setup(s => s.Get(It.IsAny())) diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs index b4e74bc74..706a02e57 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs @@ -14,28 +14,133 @@ namespace NzbDrone.Core.CustomFormats { public interface ICustomFormatCalculationService { - List ParseCustomFormat(ParsedMovieInfo movieInfo, Movie movie); + List ParseCustomFormat(RemoteMovie remoteMovie, long size); + List ParseCustomFormat(MovieFile movieFile, Movie movie); List ParseCustomFormat(MovieFile movieFile); - List ParseCustomFormat(Blocklist blocklist); - List ParseCustomFormat(MovieHistory history); + List ParseCustomFormat(Blocklist blocklist, Movie movie); + List ParseCustomFormat(MovieHistory history, Movie movie); + List ParseCustomFormat(LocalMovie localMovie); } public class CustomFormatCalculationService : ICustomFormatCalculationService { private readonly ICustomFormatService _formatService; - private readonly IParsingService _parsingService; - private readonly IMovieService _movieService; - public CustomFormatCalculationService(ICustomFormatService formatService, - IParsingService parsingService, - IMovieService movieService) + public CustomFormatCalculationService(ICustomFormatService formatService) { _formatService = formatService; - _parsingService = parsingService; - _movieService = movieService; } - public static List ParseCustomFormat(ParsedMovieInfo movieInfo, List allCustomFormats) + public List ParseCustomFormat(RemoteMovie remoteMovie, long size) + { + var input = new CustomFormatInput + { + MovieInfo = remoteMovie.ParsedMovieInfo, + Movie = remoteMovie.Movie, + Size = size, + Languages = remoteMovie.Languages + }; + + return ParseCustomFormat(input); + } + + public List ParseCustomFormat(MovieFile movieFile, Movie movie) + { + return ParseCustomFormat(movieFile, movie, _formatService.All()); + } + + public List ParseCustomFormat(MovieFile movieFile) + { + return ParseCustomFormat(movieFile, movieFile.Movie, _formatService.All()); + } + + public List ParseCustomFormat(Blocklist blocklist, Movie movie) + { + var parsed = Parser.Parser.ParseMovieTitle(blocklist.SourceTitle); + + var movieInfo = new ParsedMovieInfo + { + MovieTitles = new List() { movie.Title }, + SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? blocklist.SourceTitle.SimplifyReleaseTitle(), + ReleaseTitle = parsed?.ReleaseTitle ?? blocklist.SourceTitle, + Edition = parsed?.Edition, + Quality = blocklist.Quality, + Languages = blocklist.Languages, + ReleaseGroup = parsed?.ReleaseGroup + }; + + var input = new CustomFormatInput + { + MovieInfo = movieInfo, + Movie = movie, + Size = blocklist.Size ?? 0, + IndexerFlags = blocklist.IndexerFlags, + Languages = blocklist.Languages + }; + + return ParseCustomFormat(input); + } + + public List ParseCustomFormat(MovieHistory history, Movie movie) + { + var parsed = Parser.Parser.ParseMovieTitle(history.SourceTitle); + + long.TryParse(history.Data.GetValueOrDefault("size"), out var size); + Enum.TryParse(history.Data.GetValueOrDefault("indexerFlags"), true, out IndexerFlags flags); + + var movieInfo = new ParsedMovieInfo + { + MovieTitles = new List() { movie.Title }, + SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? history.SourceTitle.SimplifyReleaseTitle(), + ReleaseTitle = parsed?.ReleaseTitle ?? history.SourceTitle, + Edition = parsed?.Edition, + Quality = history.Quality, + Languages = history.Languages, + ReleaseGroup = parsed?.ReleaseGroup, + }; + + var input = new CustomFormatInput + { + MovieInfo = movieInfo, + Movie = movie, + Size = size, + IndexerFlags = flags, + Languages = history.Languages + }; + + return ParseCustomFormat(input); + } + + public List ParseCustomFormat(LocalMovie localMovie) + { + var episodeInfo = new ParsedMovieInfo + { + MovieTitles = new List() { localMovie.Movie.Title }, + SimpleReleaseTitle = localMovie.SceneName.SimplifyReleaseTitle(), + ReleaseTitle = localMovie.SceneName, + Quality = localMovie.Quality, + Edition = localMovie.Edition, + Languages = localMovie.Languages, + ReleaseGroup = localMovie.ReleaseGroup + }; + + var input = new CustomFormatInput + { + MovieInfo = episodeInfo, + Movie = localMovie.Movie, + Size = localMovie.Size, + Languages = localMovie.Languages + }; + + return ParseCustomFormat(input); + } + + private List ParseCustomFormat(CustomFormatInput input) + { + return ParseCustomFormat(input, _formatService.All()); + } + + private static List ParseCustomFormat(CustomFormatInput input, List allCustomFormats) { var matches = new List(); @@ -45,7 +150,7 @@ namespace NzbDrone.Core.CustomFormats .GroupBy(t => t.GetType()) .Select(g => new SpecificationMatchesGroup { - Matches = g.ToDictionary(t => t, t => t.IsSatisfiedBy(movieInfo)) + Matches = g.ToDictionary(t => t, t => t.IsSatisfiedBy(input)) }) .ToList(); @@ -58,7 +163,7 @@ namespace NzbDrone.Core.CustomFormats return matches; } - public static List ParseCustomFormat(MovieFile movieFile, List allCustomFormats) + private static List ParseCustomFormat(MovieFile movieFile, Movie movie, List allCustomFormats) { var sceneName = string.Empty; if (movieFile.SceneName.IsNotNullOrWhiteSpace()) @@ -74,90 +179,29 @@ namespace NzbDrone.Core.CustomFormats sceneName = Path.GetFileName(movieFile.RelativePath); } - var info = new ParsedMovieInfo + var movieInfo = new ParsedMovieInfo { - MovieTitles = new List() { movieFile.Movie.MovieMetadata.Value.Title }, + MovieTitles = new List() { movie.Title }, SimpleReleaseTitle = sceneName.SimplifyReleaseTitle(), Quality = movieFile.Quality, Languages = movieFile.Languages, ReleaseGroup = movieFile.ReleaseGroup, Edition = movieFile.Edition, Year = movieFile.Movie.MovieMetadata.Value.Year, - ImdbId = movieFile.Movie.MovieMetadata.Value.ImdbId, - ExtraInfo = new Dictionary - { - { "IndexerFlags", movieFile.IndexerFlags }, - { "Size", movieFile.Size }, - { "Filename", Path.GetFileName(movieFile.RelativePath) }, - { "OriginalLanguage", movieFile.Movie.MovieMetadata.Value.OriginalLanguage } - } + ImdbId = movieFile.Movie.MovieMetadata.Value.ImdbId }; - return ParseCustomFormat(info, allCustomFormats); - } - - public List ParseCustomFormat(ParsedMovieInfo movieInfo, Movie movie) - { - movieInfo = _parsingService.EnhanceMovieInfo(movieInfo, new List { movie }) ?? movieInfo; - return ParseCustomFormat(movieInfo, _formatService.All()); - } - - public List ParseCustomFormat(MovieFile movieFile) - { - return ParseCustomFormat(movieFile, _formatService.All()); - } - - public List ParseCustomFormat(Blocklist blocklist) - { - var movie = _movieService.GetMovie(blocklist.MovieId); - var parsed = _parsingService.ParseMovieInfo(blocklist.SourceTitle, null); - - var info = new ParsedMovieInfo + var input = new CustomFormatInput { - MovieTitles = new List() { movie.MovieMetadata.Value.Title }, - SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? blocklist.SourceTitle.SimplifyReleaseTitle(), - Quality = blocklist.Quality, - Languages = blocklist.Languages, - ReleaseGroup = parsed?.ReleaseGroup, - Edition = parsed?.Edition, - Year = movie.MovieMetadata.Value.Year, - ImdbId = movie.MovieMetadata.Value.ImdbId, - ExtraInfo = new Dictionary - { - { "IndexerFlags", blocklist.IndexerFlags }, - { "Size", blocklist.Size } - } - }; - - return ParseCustomFormat(info, movie); - } - - public List ParseCustomFormat(MovieHistory history) - { - var movie = _movieService.GetMovie(history.MovieId); - var parsed = _parsingService.ParseMovieInfo(history.SourceTitle, null); - - Enum.TryParse(history.Data.GetValueOrDefault("indexerFlags"), true, out IndexerFlags flags); - long.TryParse(history.Data.GetValueOrDefault("size"), out var size); - - var info = new ParsedMovieInfo - { - MovieTitles = new List() { movie.MovieMetadata.Value.Title }, - SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? history.SourceTitle.SimplifyReleaseTitle(), - Quality = history.Quality, - Languages = history.Languages, - ReleaseGroup = parsed?.ReleaseGroup, - Edition = parsed?.Edition, - Year = movie.MovieMetadata.Value.Year, - ImdbId = movie.MovieMetadata.Value.ImdbId, - ExtraInfo = new Dictionary - { - { "IndexerFlags", flags }, - { "Size", size } - } + MovieInfo = movieInfo, + Movie = movie, + Size = movieFile.Size, + IndexerFlags = movieFile.IndexerFlags, + Languages = movieFile.Languages, + Filename = Path.GetFileName(movieFile.RelativePath) }; - return ParseCustomFormat(info, movie); + return ParseCustomFormat(input, allCustomFormats); } } } diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatInput.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatInput.cs new file mode 100644 index 000000000..996af2a5f --- /dev/null +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatInput.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using NzbDrone.Core.Languages; +using NzbDrone.Core.Movies; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.CustomFormats +{ + public class CustomFormatInput + { + public ParsedMovieInfo MovieInfo { get; set; } + public Movie Movie { get; set; } + public long Size { get; set; } + public IndexerFlags IndexerFlags { get; set; } + public List Languages { get; set; } + public string Filename { get; set; } + + public CustomFormatInput() + { + Languages = new List(); + } + } +} diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/CustomFormatSpecificationBase.cs b/src/NzbDrone.Core/CustomFormats/Specifications/CustomFormatSpecificationBase.cs index 8b3610269..cd4f40309 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/CustomFormatSpecificationBase.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/CustomFormatSpecificationBase.cs @@ -21,9 +21,9 @@ namespace NzbDrone.Core.CustomFormats public abstract NzbDroneValidationResult Validate(); - public bool IsSatisfiedBy(ParsedMovieInfo movieInfo) + public bool IsSatisfiedBy(CustomFormatInput input) { - var match = IsSatisfiedByWithoutNegate(movieInfo); + var match = IsSatisfiedByWithoutNegate(input); if (Negate) { match = !match; @@ -32,6 +32,6 @@ namespace NzbDrone.Core.CustomFormats return match; } - protected abstract bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo); + protected abstract bool IsSatisfiedByWithoutNegate(CustomFormatInput input); } } diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/EditionSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/EditionSpecification.cs index 4caa607ad..e3120d48d 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/EditionSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/EditionSpecification.cs @@ -1,5 +1,3 @@ -using NzbDrone.Core.Parser.Model; - namespace NzbDrone.Core.CustomFormats { public class EditionSpecification : RegexSpecificationBase @@ -8,9 +6,9 @@ namespace NzbDrone.Core.CustomFormats public override string ImplementationName => "Edition"; public override string InfoLink => "https://wiki.servarr.com/radarr/settings#custom-formats-2"; - protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) + protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input) { - return MatchString(movieInfo.Edition); + return MatchString(input.MovieInfo.Edition); } } } diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/ICustomFormatSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/ICustomFormatSpecification.cs index d15188f9f..1ccdff791 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/ICustomFormatSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/ICustomFormatSpecification.cs @@ -15,6 +15,6 @@ namespace NzbDrone.Core.CustomFormats NzbDroneValidationResult Validate(); ICustomFormatSpecification Clone(); - bool IsSatisfiedBy(ParsedMovieInfo movieInfo); + bool IsSatisfiedBy(CustomFormatInput input); } } diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/IndexerFlagSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/IndexerFlagSpecification.cs index 47efd2cf4..4af7aef16 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/IndexerFlagSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/IndexerFlagSpecification.cs @@ -32,10 +32,9 @@ namespace NzbDrone.Core.CustomFormats [FieldDefinition(1, Label = "Flag", Type = FieldType.Select, SelectOptions = typeof(IndexerFlags))] public int Value { get; set; } - protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) + protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input) { - var flags = movieInfo?.ExtraInfo?.GetValueOrDefault("IndexerFlags") as IndexerFlags?; - return flags?.HasFlag((IndexerFlags)Value) == true; + return input.IndexerFlags.HasFlag((IndexerFlags)Value) == true; } public override NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/LanguageSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/LanguageSpecification.cs index 233bde3b8..c3b0a638a 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/LanguageSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/LanguageSpecification.cs @@ -32,12 +32,12 @@ namespace NzbDrone.Core.CustomFormats [FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))] public int Value { get; set; } - protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) + protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input) { - var comparedLanguage = movieInfo != null && Value == Language.Original.Id && movieInfo.ExtraInfo.ContainsKey("OriginalLanguage") - ? (Language)movieInfo.ExtraInfo["OriginalLanguage"] + var comparedLanguage = input.MovieInfo != null && Value == Language.Original.Id && input.Movie.MovieMetadata.Value.OriginalLanguage != Language.Unknown + ? input.Movie.MovieMetadata.Value.OriginalLanguage : (Language)Value; - return movieInfo?.Languages?.Contains(comparedLanguage) ?? false; + return input?.Languages?.Contains(comparedLanguage) ?? false; } public override NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/QualityModifierSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/QualityModifierSpecification.cs index 21db0a515..e9d4b03ee 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/QualityModifierSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/QualityModifierSpecification.cs @@ -32,9 +32,9 @@ namespace NzbDrone.Core.CustomFormats [FieldDefinition(1, Label = "Quality Modifier", Type = FieldType.Select, SelectOptions = typeof(Modifier))] public int Value { get; set; } - protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) + protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input) { - return (movieInfo?.Quality?.Quality?.Modifier ?? (int)Modifier.NONE) == (Modifier)Value; + return (input.MovieInfo?.Quality?.Quality?.Modifier ?? (int)Modifier.NONE) == (Modifier)Value; } public override NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/ReleaseGroupSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/ReleaseGroupSpecification.cs index 72319dad7..87bf87977 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/ReleaseGroupSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/ReleaseGroupSpecification.cs @@ -8,9 +8,9 @@ namespace NzbDrone.Core.CustomFormats public override string ImplementationName => "Release Group"; public override string InfoLink => "https://wiki.servarr.com/radarr/settings#custom-formats-2"; - protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) + protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input) { - return MatchString(movieInfo?.ReleaseGroup); + return MatchString(input.MovieInfo?.ReleaseGroup); } } } diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/ReleaseTitleSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/ReleaseTitleSpecification.cs index af3202c9f..6873bc2c1 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/ReleaseTitleSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/ReleaseTitleSpecification.cs @@ -9,11 +9,9 @@ namespace NzbDrone.Core.CustomFormats public override string ImplementationName => "Release Title"; public override string InfoLink => "https://wiki.servarr.com/radarr/settings#custom-formats-2"; - protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) + protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input) { - var filename = (string)movieInfo?.ExtraInfo?.GetValueOrDefault("Filename"); - - return MatchString(movieInfo?.SimpleReleaseTitle) || MatchString(filename); + return MatchString(input.MovieInfo?.SimpleReleaseTitle) || MatchString(input.Filename); } } } diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/ResolutionSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/ResolutionSpecification.cs index 5bcd1b72f..00878c846 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/ResolutionSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/ResolutionSpecification.cs @@ -24,9 +24,9 @@ namespace NzbDrone.Core.CustomFormats [FieldDefinition(1, Label = "Resolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))] public int Value { get; set; } - protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) + protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input) { - return (movieInfo?.Quality?.Quality?.Resolution ?? (int)Resolution.Unknown) == Value; + return (input.MovieInfo?.Quality?.Quality?.Resolution ?? (int)Resolution.Unknown) == Value; } public override NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/SizeSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/SizeSpecification.cs index f731a4559..7b3f91f46 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/SizeSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/SizeSpecification.cs @@ -28,9 +28,9 @@ namespace NzbDrone.Core.CustomFormats [FieldDefinition(1, Label = "Maximum Size", HelpText = "Release must be less than or equal to this size", Unit = "GB", Type = FieldType.Number)] public double Max { get; set; } - protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) + protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input) { - var size = (movieInfo?.ExtraInfo?.GetValueOrDefault("Size", 0.0) as long?) ?? 0; + var size = input.Size; return size > Min.Gigabytes() && size <= Max.Gigabytes(); } diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/SourceSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/SourceSpecification.cs index b40f39823..0ff8973bf 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/SourceSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/SourceSpecification.cs @@ -24,9 +24,9 @@ namespace NzbDrone.Core.CustomFormats [FieldDefinition(1, Label = "Source", Type = FieldType.Select, SelectOptions = typeof(Source))] public int Value { get; set; } - protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) + protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input) { - return (movieInfo?.Quality?.Quality?.Source ?? (int)Source.UNKNOWN) == (Source)Value; + return (input.MovieInfo?.Quality?.Quality?.Source ?? (int)Source.UNKNOWN) == (Source)Value; } public override NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index 95ce642cb..de04eab8e 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Serializer; using NzbDrone.Core.Configuration; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine.Specifications; +using NzbDrone.Core.Download.Aggregation; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Languages; using NzbDrone.Core.Parser; @@ -28,18 +29,21 @@ namespace NzbDrone.Core.DecisionEngine private readonly IParsingService _parsingService; private readonly IConfigService _configService; private readonly ICustomFormatCalculationService _formatCalculator; + private readonly IRemoteMovieAggregationService _aggregationService; private readonly Logger _logger; public DownloadDecisionMaker(IEnumerable specifications, IParsingService parsingService, IConfigService configService, ICustomFormatCalculationService formatCalculator, + IRemoteMovieAggregationService aggregationService, Logger logger) { _specifications = specifications; _parsingService = parsingService; _configService = configService; _formatCalculator = formatCalculator; + _aggregationService = aggregationService; _logger = logger; } @@ -104,11 +108,11 @@ namespace NzbDrone.Core.DecisionEngine result.ReleaseName = report.Title; var remoteMovie = result.RemoteMovie; - remoteMovie.CustomFormats = _formatCalculator.ParseCustomFormat(parsedMovieInfo, result?.Movie); - remoteMovie.CustomFormatScore = remoteMovie?.Movie?.Profile?.CalculateCustomFormatScore(remoteMovie.CustomFormats) ?? 0; remoteMovie.Release = report; remoteMovie.MappingResult = result.MappingResultType; + _aggregationService.Augment(remoteMovie); + if (result.MappingResultType != MappingResultType.Success) { var rejection = result.ToRejection(); @@ -116,33 +120,11 @@ namespace NzbDrone.Core.DecisionEngine } else { - if (parsedMovieInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace()) - { - // remoteMovie.DownloadAllowed = true; - if (_configService.AllowHardcodedSubs) - { - decision = GetDecisionForReport(remoteMovie, searchCriteria); - } - else - { - var whitelisted = _configService.WhitelistedHardcodedSubs.Split(','); - _logger.Debug("Testing: {0}", whitelisted); - if (whitelisted != null && whitelisted.Any(t => (parsedMovieInfo.Quality.HardcodedSubs.ToLower().Contains(t.ToLower()) && t.IsNotNullOrWhiteSpace()))) - { - decision = GetDecisionForReport(remoteMovie, searchCriteria); - } - else - { - decision = new DownloadDecision(remoteMovie, new Rejection("Hardcoded subs found: " + parsedMovieInfo.Quality.HardcodedSubs)); - } - } - } - else - { - // _aggregationService.Augment(remoteMovie); - remoteMovie.DownloadAllowed = remoteMovie.Movie != null; - decision = GetDecisionForReport(remoteMovie, searchCriteria); - } + remoteMovie.CustomFormats = _formatCalculator.ParseCustomFormat(remoteMovie, remoteMovie.Release.Size); + remoteMovie.CustomFormatScore = remoteMovie?.Movie?.Profile?.CalculateCustomFormatScore(remoteMovie.CustomFormats) ?? 0; + + remoteMovie.DownloadAllowed = remoteMovie.Movie != null; + decision = GetDecisionForReport(remoteMovie, searchCriteria); } } catch (Exception e) diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/HardcodeSubsSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/HardcodeSubsSpecification.cs new file mode 100644 index 000000000..16fd5efc4 --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/HardcodeSubsSpecification.cs @@ -0,0 +1,47 @@ +using System.Linq; +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.DecisionEngine.Specifications +{ + public class HardcodeSubsSpecification : IDecisionEngineSpecification + { + private readonly IConfigService _configService; + private readonly Logger _logger; + + public HardcodeSubsSpecification(IConfigService configService, Logger logger) + { + _configService = configService; + _logger = logger; + } + + public SpecificationPriority Priority => SpecificationPriority.Default; + public RejectionType Type => RejectionType.Permanent; + + public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + { + var hardcodeSubs = subject.ParsedMovieInfo.HardcodedSubs; + + if (_configService.AllowHardcodedSubs || hardcodeSubs.IsNullOrWhiteSpace()) + { + return Decision.Accept(); + } + + var whitelisted = _configService.WhitelistedHardcodedSubs.Split(','); + + if (whitelisted != null && whitelisted.Any(t => (hardcodeSubs.ToLower().Contains(t.ToLower()) && t.IsNotNullOrWhiteSpace()))) + { + _logger.Debug("Release hardcode subs ({0}) are in allowed values ({1})", hardcodeSubs, whitelisted); + return Decision.Accept(); + } + else + { + _logger.Debug("Hardcode subs found: {0}", hardcodeSubs); + return Decision.Reject("Hardcode subs found: {0}", hardcodeSubs); + } + } + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs index 3ac2506e3..446d4cf5d 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs @@ -50,7 +50,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications continue; } - var customFormats = _formatService.ParseCustomFormat(remoteMovie.ParsedMovieInfo, subject.Movie); + var customFormats = _formatService.ParseCustomFormat(remoteMovie, (long)queueItem.Size); _logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0} - {1}", remoteMovie.ParsedMovieInfo.Quality, diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs index 2fc65c0e9..8f84f3d0e 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs @@ -49,7 +49,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync if (mostRecent != null && mostRecent.EventType == MovieHistoryEventType.Grabbed) { - var customFormats = _formatService.ParseCustomFormat(mostRecent); + var customFormats = _formatService.ParseCustomFormat(mostRecent, subject.Movie); var cutoffUnmet = _upgradableSpecification.CutoffNotMet(subject.Movie.Profile, mostRecent.Quality, diff --git a/src/NzbDrone.Core/Download/Aggregation/Aggregators/AggregateLanguages.cs b/src/NzbDrone.Core/Download/Aggregation/Aggregators/AggregateLanguages.cs new file mode 100644 index 000000000..010a2ec16 --- /dev/null +++ b/src/NzbDrone.Core/Download/Aggregation/Aggregators/AggregateLanguages.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NLog; +using NzbDrone.Core.Languages; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Download.Aggregation.Aggregators +{ + public class AggregateLanguages : IAggregateRemoteMovie + { + private readonly Logger _logger; + + public AggregateLanguages(Logger logger) + { + _logger = logger; + } + + public RemoteMovie Aggregate(RemoteMovie remoteMovie) + { + var parsedMovieInfo = remoteMovie.ParsedMovieInfo; + var languages = parsedMovieInfo.Languages; + var movie = remoteMovie.Movie; + var releaseTokens = parsedMovieInfo.SimpleReleaseTitle ?? parsedMovieInfo.ReleaseTitle; + var normalizedReleaseTokens = Parser.Parser.NormalizeEpisodeTitle(releaseTokens); + var languagesToRemove = new List(); + + if (movie == null) + { + _logger.Debug("Unable to aggregate languages, using parsed values: {0}", string.Join(", ", languages.ToList())); + + remoteMovie.Languages = languages; + + return remoteMovie; + } + + var movieTitleLanguage = LanguageParser.ParseLanguages(movie.Title); + + if (!movieTitleLanguage.Contains(Language.Unknown)) + { + var normalizedEpisodeTitle = Parser.Parser.NormalizeEpisodeTitle(movie.Title); + var movieTitleIndex = normalizedReleaseTokens.IndexOf(normalizedEpisodeTitle, StringComparison.CurrentCultureIgnoreCase); + + if (movieTitleIndex >= 0) + { + releaseTokens = releaseTokens.Remove(movieTitleIndex, normalizedEpisodeTitle.Length); + languagesToRemove.AddRange(movieTitleLanguage); + } + } + + // Remove any languages still in the title that would normally be removed + languagesToRemove = languagesToRemove.Except(LanguageParser.ParseLanguages(releaseTokens)).ToList(); + + // Remove all languages that aren't part of the updated releaseTokens + languages = languages.Except(languagesToRemove).ToList(); + + // Use movie language as fallback if we couldn't parse a language + if (languages.Count == 0 || (languages.Count == 1 && languages.First() == Language.Unknown)) + { + languages = new List { movie.MovieMetadata.Value.OriginalLanguage }; + _logger.Debug("Language couldn't be parsed from release, fallback to movie original language: {0}", movie.MovieMetadata.Value.OriginalLanguage.Name); + } + + if (languages.Contains(Language.Original)) + { + languages.Remove(Language.Original); + + if (!languages.Contains(movie.MovieMetadata.Value.OriginalLanguage)) + { + languages.Add(movie.MovieMetadata.Value.OriginalLanguage); + } + else + { + languages.Add(Language.Unknown); + } + } + + _logger.Debug("Selected languages: {0}", string.Join(", ", languages.ToList())); + + remoteMovie.Languages = languages; + + return remoteMovie; + } + } +} diff --git a/src/NzbDrone.Core/Download/Aggregation/Aggregators/IAggregateRemoteMovie.cs b/src/NzbDrone.Core/Download/Aggregation/Aggregators/IAggregateRemoteMovie.cs new file mode 100644 index 000000000..950717831 --- /dev/null +++ b/src/NzbDrone.Core/Download/Aggregation/Aggregators/IAggregateRemoteMovie.cs @@ -0,0 +1,9 @@ +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Download.Aggregation.Aggregators +{ + public interface IAggregateRemoteMovie + { + RemoteMovie Aggregate(RemoteMovie remoteMovie); + } +} diff --git a/src/NzbDrone.Core/Download/Aggregation/RemoteMovieAggregationService.cs b/src/NzbDrone.Core/Download/Aggregation/RemoteMovieAggregationService.cs new file mode 100644 index 000000000..46d635d6e --- /dev/null +++ b/src/NzbDrone.Core/Download/Aggregation/RemoteMovieAggregationService.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using NLog; +using NzbDrone.Core.Download.Aggregation.Aggregators; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Download.Aggregation +{ + public interface IRemoteMovieAggregationService + { + RemoteMovie Augment(RemoteMovie remoteMovie); + } + + public class RemoteMovieAggregationService : IRemoteMovieAggregationService + { + private readonly IEnumerable _augmenters; + private readonly Logger _logger; + + public RemoteMovieAggregationService(IEnumerable augmenters, + Logger logger) + { + _augmenters = augmenters; + _logger = logger; + } + + public RemoteMovie Augment(RemoteMovie remoteMovie) + { + foreach (var augmenter in _augmenters) + { + try + { + augmenter.Aggregate(remoteMovie); + } + catch (Exception ex) + { + _logger.Warn(ex, ex.Message); + } + } + + return remoteMovie; + } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs index 07383a89b..66802213d 100644 --- a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs +++ b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs @@ -58,7 +58,7 @@ namespace NzbDrone.Core.Download.Clients.Flood result.Add(remoteMovie.ParsedMovieInfo.Quality.Quality.ToString()); break; case (int)AdditionalTags.Languages: - result.UnionWith(remoteMovie.ParsedMovieInfo.Languages.ConvertAll(language => language.ToString())); + result.UnionWith(remoteMovie.Languages.ConvertAll(language => language.ToString())); break; case (int)AdditionalTags.ReleaseGroup: result.Add(remoteMovie.ParsedMovieInfo.ReleaseGroup); diff --git a/src/NzbDrone.Core/Download/IgnoredDownloadService.cs b/src/NzbDrone.Core/Download/IgnoredDownloadService.cs index d47b9146b..57ecd5d7e 100644 --- a/src/NzbDrone.Core/Download/IgnoredDownloadService.cs +++ b/src/NzbDrone.Core/Download/IgnoredDownloadService.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Download var downloadIgnoredEvent = new DownloadIgnoredEvent { MovieId = movie.Id, - Languages = trackedDownload.RemoteMovie.ParsedMovieInfo.Languages, + Languages = trackedDownload.RemoteMovie.Languages, Quality = trackedDownload.RemoteMovie.ParsedMovieInfo.Quality, SourceTitle = trackedDownload.DownloadItem.Title, DownloadClientInfo = trackedDownload.DownloadItem.DownloadClientInfo, diff --git a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs index ab517356d..8b509c2a3 100644 --- a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs +++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.Download.Aggregation; using NzbDrone.Core.Indexers; using NzbDrone.Core.Jobs; using NzbDrone.Core.Languages; @@ -44,6 +45,7 @@ namespace NzbDrone.Core.Download.Pending private readonly IDelayProfileService _delayProfileService; private readonly ITaskManager _taskManager; private readonly IConfigService _configService; + private readonly IRemoteMovieAggregationService _aggregationService; private readonly ICustomFormatCalculationService _formatCalculator; private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; @@ -55,6 +57,7 @@ namespace NzbDrone.Core.Download.Pending IDelayProfileService delayProfileService, ITaskManager taskManager, IConfigService configService, + IRemoteMovieAggregationService aggregationService, ICustomFormatCalculationService formatCalculator, IEventAggregator eventAggregator, Logger logger) @@ -66,6 +69,7 @@ namespace NzbDrone.Core.Download.Pending _delayProfileService = delayProfileService; _taskManager = taskManager; _configService = configService; + _aggregationService = aggregationService; _formatCalculator = formatCalculator; _eventAggregator = eventAggregator; _logger = logger; @@ -163,8 +167,6 @@ namespace NzbDrone.Core.Download.Pending { if (pendingRelease.RemoteMovie != null) { - pendingRelease.RemoteMovie.CustomFormats = _formatCalculator.ParseCustomFormat(pendingRelease.ParsedMovieInfo, pendingRelease.RemoteMovie.Movie); - var ect = pendingRelease.Release.PublishDate.AddMinutes(GetDelay(pendingRelease.RemoteMovie)); if (ect < nextRssSync.Value) @@ -188,7 +190,7 @@ namespace NzbDrone.Core.Download.Pending Id = GetQueueId(pendingRelease, pendingRelease.RemoteMovie.Movie), Movie = pendingRelease.RemoteMovie.Movie, Quality = pendingRelease.RemoteMovie.ParsedMovieInfo?.Quality ?? new QualityModel(), - Languages = pendingRelease.RemoteMovie.ParsedMovieInfo?.Languages ?? new List(), + Languages = pendingRelease.RemoteMovie.Languages, Title = pendingRelease.Title, Size = pendingRelease.RemoteMovie.Release.Size, Sizeleft = pendingRelease.RemoteMovie.Release.Size, @@ -296,6 +298,9 @@ namespace NzbDrone.Core.Download.Pending Release = release.Release }; + _aggregationService.Augment(release.RemoteMovie); + release.RemoteMovie.CustomFormats = _formatCalculator.ParseCustomFormat(release.RemoteMovie, release.Release.Size); + result.Add(release); } diff --git a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs index ef2e36540..243f77f1b 100644 --- a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs +++ b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs @@ -6,6 +6,7 @@ using NzbDrone.Common.Cache; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.CustomFormats; +using NzbDrone.Core.Download.Aggregation; using NzbDrone.Core.Download.History; using NzbDrone.Core.History; using NzbDrone.Core.Messaging.Events; @@ -32,6 +33,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads private readonly IEventAggregator _eventAggregator; private readonly IDownloadHistoryService _downloadHistoryService; private readonly IConfigService _config; + private readonly IRemoteMovieAggregationService _aggregationService; private readonly ICustomFormatCalculationService _formatCalculator; private readonly Logger _logger; private readonly ICached _cache; @@ -40,6 +42,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads ICacheManager cacheManager, IHistoryService historyService, IConfigService config, + IRemoteMovieAggregationService aggregationService, ICustomFormatCalculationService formatCalculator, IEventAggregator eventAggregator, IDownloadHistoryService downloadHistoryService, @@ -49,6 +52,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads _historyService = historyService; _cache = cacheManager.GetCache(GetType()); _config = config; + _aggregationService = aggregationService; _formatCalculator = formatCalculator; _eventAggregator = eventAggregator; _downloadHistoryService = downloadHistoryService; @@ -118,6 +122,8 @@ namespace NzbDrone.Core.Download.TrackedDownloads if (parsedMovieInfo != null) { trackedDownload.RemoteMovie = _parsingService.Map(parsedMovieInfo, "", null).RemoteMovie; + + _aggregationService.Augment(trackedDownload.RemoteMovie); } var downloadHistory = _downloadHistoryService.GetLatestDownloadHistoryItem(downloadItem.DownloadId); @@ -151,7 +157,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads // Calculate custom formats if (trackedDownload.RemoteMovie != null) { - trackedDownload.RemoteMovie.CustomFormats = _formatCalculator.ParseCustomFormat(parsedMovieInfo, trackedDownload.RemoteMovie.Movie); + trackedDownload.RemoteMovie.CustomFormats = _formatCalculator.ParseCustomFormat(trackedDownload.RemoteMovie, downloadItem.TotalSize); } // Track it so it can be displayed in the queue even though we can't determine which movie it is for @@ -192,6 +198,8 @@ namespace NzbDrone.Core.Download.TrackedDownloads var parsedMovieInfo = Parser.Parser.ParseMovieTitle(trackedDownload.DownloadItem.Title); trackedDownload.RemoteMovie = parsedMovieInfo == null ? null : _parsingService.Map(parsedMovieInfo, "", null).RemoteMovie; + + _aggregationService.Augment(trackedDownload.RemoteMovie); } private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType eventType) diff --git a/src/NzbDrone.Core/History/HistoryService.cs b/src/NzbDrone.Core/History/HistoryService.cs index 0994d04ec..8c25c7826 100644 --- a/src/NzbDrone.Core/History/HistoryService.cs +++ b/src/NzbDrone.Core/History/HistoryService.cs @@ -132,7 +132,7 @@ namespace NzbDrone.Core.History EventType = MovieHistoryEventType.Grabbed, Date = DateTime.UtcNow, Quality = message.Movie.ParsedMovieInfo.Quality, - Languages = message.Movie.ParsedMovieInfo.Languages, + Languages = message.Movie.Languages, SourceTitle = message.Movie.Release.Title, DownloadId = message.DownloadId, MovieId = message.Movie.Movie.Id @@ -203,6 +203,7 @@ namespace NzbDrone.Core.History history.Data.Add("DownloadClient", message.DownloadClientInfo?.Type); history.Data.Add("DownloadClientName", message.DownloadClientInfo?.Name); history.Data.Add("ReleaseGroup", message.MovieInfo.ReleaseGroup); + history.Data.Add("CustomFormatScore", message.MovieInfo.CustomFormatScore.ToString()); _historyRepository.Insert(history); } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecisionMaker.cs index ad9d8d284..031ee104c 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecisionMaker.cs @@ -4,6 +4,7 @@ using System.Linq; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; +using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.MovieImport.Aggregation; @@ -30,6 +31,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport private readonly IDiskProvider _diskProvider; private readonly IDetectSample _detectSample; private readonly IParsingService _parsingService; + private readonly ICustomFormatCalculationService _formatCalculator; private readonly Logger _logger; public ImportDecisionMaker(IEnumerable specifications, @@ -38,6 +40,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport IDiskProvider diskProvider, IDetectSample detectSample, IParsingService parsingService, + ICustomFormatCalculationService formatCalculator, Logger logger) { _specifications = specifications; @@ -46,6 +49,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport _diskProvider = diskProvider; _detectSample = detectSample; _parsingService = parsingService; + _formatCalculator = formatCalculator; _logger = logger; } @@ -75,7 +79,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport if (downloadClientItem != null) { downloadClientItemInfo = Parser.Parser.ParseMovieTitle(downloadClientItem.Title); - downloadClientItemInfo = _parsingService.EnhanceMovieInfo(downloadClientItemInfo); } var nonSampleVideoFileCount = GetNonSampleVideoFileCount(newFiles, movie.MovieMetadata); @@ -115,11 +118,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport var fileMovieInfo = Parser.Parser.ParseMoviePath(localMovie.Path); - if (fileMovieInfo != null) - { - fileMovieInfo = _parsingService.EnhanceMovieInfo(fileMovieInfo); - } - localMovie.FileMovieInfo = fileMovieInfo; localMovie.Size = _diskProvider.GetFileSize(localMovie.Path); @@ -133,6 +131,9 @@ namespace NzbDrone.Core.MediaFiles.MovieImport } else { + localMovie.CustomFormats = _formatCalculator.ParseCustomFormat(localMovie); + localMovie.CustomFormatScore = localMovie.Movie.Profile?.CalculateCustomFormatScore(localMovie.CustomFormats) ?? 0; + decision = GetDecision(localMovie, downloadClientItem); } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportItem.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportItem.cs index 6ed20c180..2f19a99f4 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportItem.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportItem.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Languages; using NzbDrone.Core.Movies; @@ -17,7 +18,13 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual public List Languages { get; set; } public string ReleaseGroup { get; set; } public string DownloadId { get; set; } + public List CustomFormats { get; set; } public IEnumerable Rejections { get; set; } public Movie Movie { get; set; } + + public ManualImportItem() + { + CustomFormats = new List(); + } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs index b4c997dbd..9c5681e03 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs @@ -6,6 +6,7 @@ using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Download.TrackedDownloads; @@ -37,6 +38,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual private readonly IAggregationService _aggregationService; private readonly ITrackedDownloadService _trackedDownloadService; private readonly IDownloadedMovieImportService _downloadedMovieImportService; + private readonly ICustomFormatCalculationService _formatCalculator; private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; @@ -49,6 +51,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual IImportApprovedMovie importApprovedMovie, ITrackedDownloadService trackedDownloadService, IDownloadedMovieImportService downloadedMovieImportService, + ICustomFormatCalculationService formatCalculator, IEventAggregator eventAggregator, Logger logger) { @@ -61,6 +64,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual _importApprovedMovie = importApprovedMovie; _trackedDownloadService = trackedDownloadService; _downloadedMovieImportService = downloadedMovieImportService; + _formatCalculator = formatCalculator; _eventAggregator = eventAggregator; _logger = logger; } @@ -291,6 +295,8 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual if (decision.LocalMovie.Movie != null) { item.Movie = decision.LocalMovie.Movie; + + item.CustomFormats = _formatCalculator.ParseCustomFormat(decision.LocalMovie); } item.Quality = decision.LocalMovie.Quality; @@ -343,7 +349,10 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual localMovie.SceneSource = !existingFile; } + // Augment movie file so imported files have all additional information an automatic import would localMovie = _aggregationService.Augment(localMovie, trackedDownload?.DownloadItem); + localMovie.CustomFormats = _formatCalculator.ParseCustomFormat(localMovie); + localMovie.CustomFormatScore = localMovie.Movie.Profile?.CalculateCustomFormatScore(localMovie.CustomFormats) ?? 0; // Apply the user-chosen values. localMovie.Movie = movie; diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs index f05a5dcf4..7fa64e64b 100755 --- a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs +++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs @@ -118,6 +118,8 @@ namespace NzbDrone.Core.Notifications.CustomScript environmentVariables.Add("Radarr_MovieFile_MediaInfo_Subtitles", movieFile.MediaInfo.Subtitles.ConcatToString(" / ")); environmentVariables.Add("Radarr_MovieFile_MediaInfo_VideoCodec", MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, null)); environmentVariables.Add("Radarr_MovieFile_MediaInfo_VideoDynamicRangeType", MediaInfoFormatter.FormatVideoDynamicRangeType(movieFile.MediaInfo)); + environmentVariables.Add("Radarr_MovieFile_CustomFormat", string.Join("|", message.MovieInfo.CustomFormats)); + environmentVariables.Add("Radarr_MovieFile_CustomFormatScore", message.MovieInfo.CustomFormatScore.ToString()); if (message.OldMovieFiles.Any()) { diff --git a/src/NzbDrone.Core/Notifications/DownloadMessage.cs b/src/NzbDrone.Core/Notifications/DownloadMessage.cs index a11a1827c..64da38bdb 100644 --- a/src/NzbDrone.Core/Notifications/DownloadMessage.cs +++ b/src/NzbDrone.Core/Notifications/DownloadMessage.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Movies; +using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Notifications { @@ -9,6 +10,7 @@ namespace NzbDrone.Core.Notifications { public string Message { get; set; } public Movie Movie { get; set; } + public LocalMovie MovieInfo { get; set; } public MovieFile MovieFile { get; set; } public List OldMovieFiles { get; set; } public string SourcePath { get; set; } diff --git a/src/NzbDrone.Core/Notifications/NotificationService.cs b/src/NzbDrone.Core/Notifications/NotificationService.cs index adb2aa822..141bbc848 100644 --- a/src/NzbDrone.Core/Notifications/NotificationService.cs +++ b/src/NzbDrone.Core/Notifications/NotificationService.cs @@ -130,6 +130,7 @@ namespace NzbDrone.Core.Notifications var downloadMessage = new DownloadMessage { Message = GetMessage(message.MovieInfo.Movie, message.MovieInfo.Quality), + MovieInfo = message.MovieInfo, MovieFile = message.ImportedMovie, Movie = message.MovieInfo.Movie, OldMovieFiles = message.OldFiles, diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs index 8f9686db0..50a792765 100644 --- a/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs @@ -36,7 +36,8 @@ namespace NzbDrone.Core.Notifications.Webhook Release = new WebhookRelease(quality, remoteMovie), DownloadClient = message.DownloadClientName, DownloadClientType = message.DownloadClientType, - DownloadId = message.DownloadId + DownloadId = message.DownloadId, + CustomFormatInfo = new WebhookCustomFormatInfo(remoteMovie.CustomFormats, remoteMovie.CustomFormatScore) }; } @@ -55,7 +56,8 @@ namespace NzbDrone.Core.Notifications.Webhook IsUpgrade = message.OldMovieFiles.Any(), DownloadClient = message.DownloadClientInfo?.Name, DownloadClientType = message.DownloadClientInfo?.Type, - DownloadId = message.DownloadId + DownloadId = message.DownloadId, + CustomFormatInfo = new WebhookCustomFormatInfo(message.MovieInfo.CustomFormats, message.MovieInfo.CustomFormatScore) }; if (message.OldMovieFiles.Any()) diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookCustomFormat.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookCustomFormat.cs new file mode 100644 index 000000000..192137f43 --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookCustomFormat.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using NzbDrone.Core.CustomFormats; + +namespace NzbDrone.Core.Notifications.Webhook +{ + public class WebhookCustomFormat + { + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public int Id { get; set; } + public string Name { get; set; } + + public WebhookCustomFormat(CustomFormat customFormat) + { + Id = customFormat.Id; + Name = customFormat.Name; + } + } +} diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookCustomFormatInfo.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookCustomFormatInfo.cs new file mode 100644 index 000000000..81a88e87b --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookCustomFormatInfo.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Core.CustomFormats; + +namespace NzbDrone.Core.Notifications.Webhook +{ + public class WebhookCustomFormatInfo + { + public List CustomFormats { get; set; } + public int CustomFormatScore { get; set; } + + public WebhookCustomFormatInfo(List customFormats, int customFormatScore) + { + CustomFormats = customFormats.Select(c => new WebhookCustomFormat(c)).ToList(); + CustomFormatScore = customFormatScore; + } + } +} diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabPayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabPayload.cs index 431fa5558..f43f8b8c3 100755 --- a/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabPayload.cs +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabPayload.cs @@ -8,5 +8,6 @@ namespace NzbDrone.Core.Notifications.Webhook public string DownloadClient { get; set; } public string DownloadClientType { get; set; } public string DownloadId { get; set; } + public WebhookCustomFormatInfo CustomFormatInfo { get; set; } } } diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs index b4d0306b7..c28ba25f8 100755 --- a/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs @@ -12,5 +12,6 @@ namespace NzbDrone.Core.Notifications.Webhook public string DownloadClientType { get; set; } public string DownloadId { get; set; } public List DeletedFiles { get; set; } + public WebhookCustomFormatInfo CustomFormatInfo { get; set; } } } diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 0cdd580fe..115779fcc 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -35,7 +35,7 @@ namespace NzbDrone.Core.Organizer private readonly IQualityDefinitionService _qualityDefinitionService; private readonly IUpdateMediaInfo _mediaInfoUpdater; private readonly IMovieTranslationService _movieTranslationService; - private readonly ICustomFormatService _formatService; + private readonly ICustomFormatCalculationService _formatCalculator; private readonly Logger _logger; private static readonly Regex TitleRegex = new Regex(@"(?\{(?:imdb-|edition-))?\{(?[- ._\[(]*)(?(?:[a-z0-9]+)(?:(?[- ._]+)(?:[a-z0-9]+))?)(?::(?[a-z0-9|+-]+(?[-} ._)\]]*)\}", @@ -83,14 +83,14 @@ namespace NzbDrone.Core.Organizer IQualityDefinitionService qualityDefinitionService, IUpdateMediaInfo mediaInfoUpdater, IMovieTranslationService movieTranslationService, - ICustomFormatService formatService, + ICustomFormatCalculationService formatCalculator, Logger logger) { _namingConfigService = namingConfigService; _qualityDefinitionService = qualityDefinitionService; _mediaInfoUpdater = mediaInfoUpdater; _movieTranslationService = movieTranslationService; - _formatService = formatService; + _formatCalculator = formatCalculator; _logger = logger; } @@ -397,7 +397,7 @@ namespace NzbDrone.Core.Organizer if (customFormats == null) { movieFile.Movie = movie; - customFormats = CustomFormatCalculationService.ParseCustomFormat(movieFile, _formatService.All()); + customFormats = _formatCalculator.ParseCustomFormat(movieFile, movie); } tokenHandlers["{Custom Formats}"] = m => string.Join(" ", customFormats.Where(x => x.IncludeCustomFormatWhenRenaming)); diff --git a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithFileSize.cs b/src/NzbDrone.Core/Parser/Augmenters/AugmentWithFileSize.cs deleted file mode 100644 index 770eb8820..000000000 --- a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithFileSize.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Parser.Augmenters -{ - public class AugmentWithFileSize : IAugmentParsedMovieInfo - { - public Type HelperType - { - get - { - return typeof(LocalMovie); - } - } - - public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper) - { - if (helper is LocalMovie localMovie && localMovie.Size != 0) - { - movieInfo.ExtraInfo["Size"] = localMovie.Size; - } - - return movieInfo; - } - } -} diff --git a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithHistory.cs b/src/NzbDrone.Core/Parser/Augmenters/AugmentWithHistory.cs deleted file mode 100644 index d5b5c62a2..000000000 --- a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithHistory.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NzbDrone.Core.History; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Parser.Augmenters -{ - public class AugmentWithHistory : IAugmentParsedMovieInfo - { - private readonly IEnumerable> _augmenters; - - public AugmentWithHistory(IEnumerable> augmenters) - { - _augmenters = augmenters; - } - - public Type HelperType - { - get - { - return typeof(MovieHistory); - } - } - - public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper) - { - if (helper is MovieHistory history && history.EventType == MovieHistoryEventType.Grabbed) - { - // First we create a release info from history data. - var releaseInfo = new ReleaseInfo(); - - if (int.TryParse(history.Data.GetValueOrDefault("indexerId"), out var indexerId)) - { - releaseInfo.IndexerId = indexerId; - } - - if (long.TryParse(history.Data.GetValueOrDefault("size"), out var size)) - { - releaseInfo.Size = size; - } - - if (Enum.TryParse(history.Data.GetValueOrDefault("indexerFlags"), true, out IndexerFlags indexerFlags)) - { - releaseInfo.IndexerFlags = indexerFlags; - } - - // Now we run the release info augmenters from the history release info. TODO: Add setting to only do that if you trust your indexer! - var releaseInfoAugmenters = _augmenters.Where(a => a.Value.HelperType.IsInstanceOfType(releaseInfo)); - foreach (var augmenter in releaseInfoAugmenters) - { - movieInfo = augmenter.Value.AugmentMovieInfo(movieInfo, releaseInfo); - } - } - - return movieInfo; - } - } -} diff --git a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithMediaInfo.cs b/src/NzbDrone.Core/Parser/Augmenters/AugmentWithMediaInfo.cs deleted file mode 100644 index 50b88a321..000000000 --- a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithMediaInfo.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using NzbDrone.Core.MediaFiles.MediaInfo; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Qualities; - -namespace NzbDrone.Core.Parser.Augmenters -{ - public class AugmentWithMediaInfo : IAugmentParsedMovieInfo - { - public Type HelperType - { - get - { - return typeof(MediaInfoModel); - } - } - - public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper) - { - if (helper is MediaInfoModel mediaInfo) - { - var quality = movieInfo.Quality; - if (!(quality.Quality.Modifier == Modifier.BRDISK || quality.Quality.Modifier == Modifier.REMUX) && - (quality.Quality.Source == Source.BLURAY || quality.Quality.Source == Source.TV || - quality.Quality.Source == Source.WEBDL) && - !(quality.Quality.Resolution == (int)Resolution.R480p || quality.Quality.Resolution == (int)Resolution.R576p)) - { - var width = mediaInfo.Width; - var existing = quality.Quality.Resolution; - - if (width > 854) - { - quality.Quality.Resolution = (int)Resolution.R720p; - } - - if (width > 1280) - { - quality.Quality.Resolution = (int)Resolution.R1080p; - } - - if (width > 1920) - { - quality.Quality.Resolution = (int)Resolution.R2160p; - } - - if (existing != quality.Quality.Resolution) - { - // _logger.Debug("Overwriting resolution info {0} with info from media info {1}", existing, quality.Resolution); - quality.ResolutionDetectionSource = QualityDetectionSource.MediaInfo; - movieInfo.Quality = quality; - } - } - } - - return movieInfo; - } - } -} diff --git a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithOriginalLanguage.cs b/src/NzbDrone.Core/Parser/Augmenters/AugmentWithOriginalLanguage.cs deleted file mode 100644 index 2573e6adf..000000000 --- a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithOriginalLanguage.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using NzbDrone.Core.Movies; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Parser.Augmenters -{ - public class AugmentWithOriginalLanguage : IAugmentParsedMovieInfo - { - public Type HelperType - { - get - { - return typeof(Movie); - } - } - - public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper) - { - if (helper is Movie movie && movie?.MovieMetadata.Value.OriginalLanguage != null && movieInfo != null) - { - movieInfo.ExtraInfo["OriginalLanguage"] = movie.MovieMetadata.Value.OriginalLanguage; - } - - return movieInfo; - } - } -} diff --git a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithParsedMovieInfo.cs b/src/NzbDrone.Core/Parser/Augmenters/AugmentWithParsedMovieInfo.cs deleted file mode 100644 index a709e95ff..000000000 --- a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithParsedMovieInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq; -using NzbDrone.Common.Extensions; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Parser.Augmenters -{ - public class AugmentWithParsedMovieInfo : IAugmentParsedMovieInfo - { - public Type HelperType - { - get - { - return typeof(ParsedMovieInfo); - } - } - - public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper) - { - if (helper is ParsedMovieInfo otherInfo) - { - // Create union of all languages - if (otherInfo.Languages != null) - { - movieInfo.Languages = movieInfo.Languages.Union(otherInfo.Languages).Distinct().ToList(); - } - - if ((otherInfo.Edition?.Length ?? 0) > (movieInfo.Edition?.Length ?? 0)) - { - movieInfo.Edition = otherInfo.Edition; - } - - if (otherInfo.ReleaseGroup.IsNotNullOrWhiteSpace() && movieInfo.ReleaseGroup.IsNullOrWhiteSpace()) - { - movieInfo.ReleaseGroup = otherInfo.ReleaseGroup; - } - } - - return movieInfo; - } - } -} diff --git a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithReleaseInfo.cs b/src/NzbDrone.Core/Parser/Augmenters/AugmentWithReleaseInfo.cs deleted file mode 100644 index 5b86e7420..000000000 --- a/src/NzbDrone.Core/Parser/Augmenters/AugmentWithReleaseInfo.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Linq; -using NzbDrone.Common.Extensions; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Parser.Augmenters -{ - public class AugmentWithReleaseInfo : IAugmentParsedMovieInfo - { - private readonly Lazy _indexerFactory; - - public AugmentWithReleaseInfo(Lazy indexerFactory) - { - _indexerFactory = indexerFactory; - } - - public Type HelperType - { - get - { - return typeof(ReleaseInfo); - } - } - - public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper) - { - if (helper is ReleaseInfo releaseInfo) - { - IIndexerSettings indexerSettings = null; - try - { - indexerSettings = _indexerFactory.Value.Get(releaseInfo.IndexerId)?.Settings as IIndexerSettings; - } - catch (Exception) - { - // _logger.Debug("Indexer with id {0} does not exist, skipping minimum seeder checks.", subject.Release.IndexerId); - } // First, let's augment the language! - - var languageTitle = movieInfo.SimpleReleaseTitle; - if (movieInfo.PrimaryMovieTitle.IsNotNullOrWhiteSpace()) - { - if (languageTitle.ToLower().Contains("multi") && indexerSettings?.MultiLanguages?.Any() == true) - { - foreach (var i in indexerSettings.MultiLanguages) - { - var language = (Language)i; - if (!movieInfo.Languages.Contains(language)) - { - movieInfo.Languages.Add(language); - } - } - } - } - - // Next, let's add other useful info to the extra info dict - if (!movieInfo.ExtraInfo.ContainsKey("Size")) - { - movieInfo.ExtraInfo["Size"] = releaseInfo.Size; - } - - movieInfo.ExtraInfo["IndexerFlags"] = releaseInfo.IndexerFlags; - } - - return movieInfo; - } - } -} diff --git a/src/NzbDrone.Core/Parser/Augmenters/IAugmentParsedMovieInfo.cs b/src/NzbDrone.Core/Parser/Augmenters/IAugmentParsedMovieInfo.cs deleted file mode 100644 index 8a6200e68..000000000 --- a/src/NzbDrone.Core/Parser/Augmenters/IAugmentParsedMovieInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Parser.Augmenters -{ - public interface IAugmentParsedMovieInfo - { - Type HelperType { get; } - - ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper); - } -} diff --git a/src/NzbDrone.Core/Parser/Model/LocalMovie.cs b/src/NzbDrone.Core/Parser/Model/LocalMovie.cs index d2fd17780..2128cac4f 100644 --- a/src/NzbDrone.Core/Parser/Model/LocalMovie.cs +++ b/src/NzbDrone.Core/Parser/Model/LocalMovie.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Languages; using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.Movies; @@ -10,6 +11,7 @@ namespace NzbDrone.Core.Parser.Model { public LocalMovie() { + CustomFormats = new List(); } public string Path { get; set; } @@ -27,6 +29,8 @@ namespace NzbDrone.Core.Parser.Model public string Edition { get; set; } public string SceneName { get; set; } public bool OtherVideoFiles { get; set; } + public List CustomFormats { get; set; } + public int CustomFormatScore { get; set; } public override string ToString() { diff --git a/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs b/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs index 205268ccd..3cacf4779 100644 --- a/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs @@ -25,8 +25,7 @@ namespace NzbDrone.Core.Parser.Model public int Year { get; set; } public string ImdbId { get; set; } public int TmdbId { get; set; } - [JsonIgnore] - public Dictionary ExtraInfo { get; set; } = new Dictionary(); + public string HardcodedSubs { get; set; } public string MovieTitle => PrimaryMovieTitle; diff --git a/src/NzbDrone.Core/Parser/Model/RemoteMovie.cs b/src/NzbDrone.Core/Parser/Model/RemoteMovie.cs index c761d61c6..58ba9d647 100644 --- a/src/NzbDrone.Core/Parser/Model/RemoteMovie.cs +++ b/src/NzbDrone.Core/Parser/Model/RemoteMovie.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Download.Clients; +using NzbDrone.Core.Languages; using NzbDrone.Core.Movies; namespace NzbDrone.Core.Parser.Model @@ -15,6 +16,13 @@ namespace NzbDrone.Core.Parser.Model public MappingResultType MappingResult { get; set; } public bool DownloadAllowed { get; set; } public TorrentSeedConfiguration SeedConfiguration { get; set; } + public List Languages { get; set; } + + public RemoteMovie() + { + CustomFormats = new List(); + Languages = new List(); + } public override string ToString() { diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index e56859a7f..90522243d 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -18,6 +18,9 @@ namespace NzbDrone.Core.Parser private static readonly Regex ReportEditionRegex = new Regex(@"^.+?" + EditionRegex, RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex HardcodedSubsRegex = new Regex(@"\b((?(\w+(?(HC|SUBBED)))\b", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + private static readonly RegexReplace[] PreSubstitutionRegex = Array.Empty(); private static readonly Regex[] ReportMovieTitleRegex = new[] @@ -295,6 +298,8 @@ namespace NzbDrone.Core.Parser result.ReleaseGroup = subGroup; } + result.HardcodedSubs = ParseHardcodeSubs(title); + Logger.Debug("Release Group parsed: {0}", result.ReleaseGroup); result.Languages = LanguageParser.ParseLanguages(result.ReleaseGroup.IsNotNullOrWhiteSpace() ? simpleReleaseTitle.Replace(result.ReleaseGroup, "RlsGrp") : simpleReleaseTitle); @@ -491,6 +496,25 @@ namespace NzbDrone.Core.Parser return SimpleReleaseTitleRegex.Replace(title, string.Empty); } + public static string ParseHardcodeSubs(string title) + { + var subMatch = HardcodedSubsRegex.Matches(title).OfType().LastOrDefault(); + + if (subMatch != null && subMatch.Success) + { + if (subMatch.Groups["hcsub"].Success) + { + return subMatch.Groups["hcsub"].Value; + } + else if (subMatch.Groups["hc"].Success) + { + return "Generic Hardcoded Subs"; + } + } + + return null; + } + public static string ParseReleaseGroup(string title) { title = title.Trim(); diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index 5680d455f..9bb4b5a3a 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -6,7 +6,6 @@ using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Languages; using NzbDrone.Core.Movies; -using NzbDrone.Core.Parser.Augmenters; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.RomanNumerals; @@ -17,7 +16,6 @@ namespace NzbDrone.Core.Parser Movie GetMovie(string title); MappingResult Map(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchCriteriaBase searchCriteria = null); ParsedMovieInfo ParseMovieInfo(string title, List helpers); - ParsedMovieInfo EnhanceMovieInfo(ParsedMovieInfo parsedMovieInfo, List helpers = null); ParsedMovieInfo ParseMinimalMovieInfo(string path, bool isDir = false); ParsedMovieInfo ParseMinimalPathMovieInfo(string path); } @@ -27,15 +25,12 @@ namespace NzbDrone.Core.Parser private static HashSet _arabicRomanNumeralMappings; private readonly IMovieService _movieService; - private readonly IEnumerable _augmenters; private readonly Logger _logger; public ParsingService(IMovieService movieService, - IEnumerable augmenters, Logger logger) { _movieService = movieService; - _augmenters = augmenters; _logger = logger; if (_arabicRomanNumeralMappings == null) @@ -53,27 +48,9 @@ namespace NzbDrone.Core.Parser return null; } - result = EnhanceMovieInfo(result, helpers); - return result; } - public ParsedMovieInfo EnhanceMovieInfo(ParsedMovieInfo minimalInfo, List helpers = null) - { - if (helpers != null) - { - var augmenters = _augmenters.Where(a => helpers.Any(t => a.HelperType.IsInstanceOfType(t)) || a.HelperType == null); - - foreach (var augmenter in augmenters) - { - minimalInfo = augmenter.AugmentMovieInfo(minimalInfo, - helpers.FirstOrDefault(h => augmenter.HelperType.IsInstanceOfType(h))); - } - } - - return minimalInfo; - } - public ParsedMovieInfo ParseMinimalMovieInfo(string file, bool isDir = false) { return Parser.ParseMovieTitle(file, isDir); @@ -127,27 +104,6 @@ namespace NzbDrone.Core.Parser result.Movie = null; } - // Use movie language as fallback if we could't parse a language (more accurate than just using English) - if (parsedMovieInfo.Languages.Count <= 1 && parsedMovieInfo.Languages.First() == Language.Unknown && result.Movie != null) - { - parsedMovieInfo.Languages = new List { result.Movie.MovieMetadata.Value.OriginalLanguage }; - _logger.Debug("Language couldn't be parsed from release, fallback to movie original language: {0}", result.Movie.MovieMetadata.Value.OriginalLanguage.Name); - } - - if (parsedMovieInfo.Languages.Contains(Language.Original)) - { - parsedMovieInfo.Languages.Remove(Language.Original); - - if (result.Movie != null && !parsedMovieInfo.Languages.Contains(result.Movie.MovieMetadata.Value.OriginalLanguage)) - { - parsedMovieInfo.Languages.Add(result.Movie.MovieMetadata.Value.OriginalLanguage); - } - else - { - parsedMovieInfo.Languages.Add(Language.Unknown); - } - } - result.RemoteMovie.ParsedMovieInfo = parsedMovieInfo; return result; diff --git a/src/NzbDrone.Core/Parser/QualityParser.cs b/src/NzbDrone.Core/Parser/QualityParser.cs index 85245f44b..e90a6bfba 100644 --- a/src/NzbDrone.Core/Parser/QualityParser.cs +++ b/src/NzbDrone.Core/Parser/QualityParser.cs @@ -75,9 +75,6 @@ namespace NzbDrone.Core.Parser private static readonly Regex RemuxRegex = new Regex(@"(?:[_. \[]|\d{4}p-)(?(?:(BD|UHD)[-_. ]?)?Remux)\b|(?(?:(BD|UHD)[-_. ]?)?Remux[_. ]\d{4}p)", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private static readonly Regex HardcodedSubsRegex = new Regex(@"\b((?(\w+(?(HC|SUBBED)))\b", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - public static QualityModel ParseQuality(string name) { Logger.Debug("Trying to parse quality for {0}", name); @@ -110,19 +107,6 @@ namespace NzbDrone.Core.Parser { var normalizedName = name.Replace('_', ' ').Trim(); var result = ParseQualityModifiers(name, normalizedName); - var subMatch = HardcodedSubsRegex.Matches(normalizedName).OfType().LastOrDefault(); - - if (subMatch != null && subMatch.Success) - { - if (subMatch.Groups["hcsub"].Success) - { - result.HardcodedSubs = subMatch.Groups["hcsub"].Value; - } - else if (subMatch.Groups["hc"].Success) - { - result.HardcodedSubs = "Generic Hardcoded Subs"; - } - } var sourceMatches = SourceRegex.Matches(normalizedName); var sourceMatch = sourceMatches.OfType().LastOrDefault(); diff --git a/src/NzbDrone.Core/Qualities/QualityModel.cs b/src/NzbDrone.Core/Qualities/QualityModel.cs index e081b5c3a..cf4216ff1 100644 --- a/src/NzbDrone.Core/Qualities/QualityModel.cs +++ b/src/NzbDrone.Core/Qualities/QualityModel.cs @@ -10,8 +10,6 @@ namespace NzbDrone.Core.Qualities public Revision Revision { get; set; } - public string HardcodedSubs { get; set; } - [JsonIgnore] public QualityDetectionSource SourceDetectionSource { get; set; } diff --git a/src/Radarr.Api.V3/Blocklist/BlocklistController.cs b/src/Radarr.Api.V3/Blocklist/BlocklistController.cs index ee81cedff..54b0fe88c 100644 --- a/src/Radarr.Api.V3/Blocklist/BlocklistController.cs +++ b/src/Radarr.Api.V3/Blocklist/BlocklistController.cs @@ -17,13 +17,14 @@ namespace Radarr.Api.V3.Blocklist private readonly ICustomFormatCalculationService _formatCalculator; public BlocklistController(IBlocklistService blocklistService, - ICustomFormatCalculationService formatCalculator) + ICustomFormatCalculationService formatCalculator) { _blocklistService = blocklistService; _formatCalculator = formatCalculator; } [HttpGet] + [Produces("application/json")] public PagingResource GetBlocklist() { var pagingResource = Request.ReadPagingResourceFromRequest(); @@ -45,6 +46,7 @@ namespace Radarr.Api.V3.Blocklist } [HttpDelete("bulk")] + [Produces("application/json")] public object Remove([FromBody] BlocklistBulkResource resource) { _blocklistService.Delete(resource.Ids); diff --git a/src/Radarr.Api.V3/Blocklist/BlocklistResource.cs b/src/Radarr.Api.V3/Blocklist/BlocklistResource.cs index 471d78582..d6f603bf9 100644 --- a/src/Radarr.Api.V3/Blocklist/BlocklistResource.cs +++ b/src/Radarr.Api.V3/Blocklist/BlocklistResource.cs @@ -42,7 +42,7 @@ namespace Radarr.Api.V3.Blocklist SourceTitle = model.SourceTitle, Languages = model.Languages, Quality = model.Quality, - CustomFormats = formatCalculator.ParseCustomFormat(model).ToResource(), + CustomFormats = formatCalculator.ParseCustomFormat(model, model.Movie).ToResource(false), Date = model.Date, Protocol = model.Protocol, Indexer = model.Indexer, diff --git a/src/Radarr.Api.V3/CustomFormats/CustomFormatController.cs b/src/Radarr.Api.V3/CustomFormats/CustomFormatController.cs index 5ca6a15c7..0692ccd7f 100644 --- a/src/Radarr.Api.V3/CustomFormats/CustomFormatController.cs +++ b/src/Radarr.Api.V3/CustomFormats/CustomFormatController.cs @@ -45,10 +45,11 @@ namespace Radarr.Api.V3.CustomFormats protected override CustomFormatResource GetResourceById(int id) { - return _formatService.GetById(id).ToResource(); + return _formatService.GetById(id).ToResource(true); } [RestPostById] + [Consumes("application/json")] public ActionResult Create(CustomFormatResource customFormatResource) { var model = customFormatResource.ToModel(_specifications); @@ -59,6 +60,7 @@ namespace Radarr.Api.V3.CustomFormats } [RestPutById] + [Consumes("application/json")] public ActionResult Update(CustomFormatResource resource) { var model = resource.ToModel(_specifications); @@ -71,9 +73,10 @@ namespace Radarr.Api.V3.CustomFormats } [HttpGet] + [Produces("application/json")] public List GetAll() { - return _formatService.All().ToResource(); + return _formatService.All().ToResource(true); } [RestDeleteById] diff --git a/src/Radarr.Api.V3/CustomFormats/CustomFormatResource.cs b/src/Radarr.Api.V3/CustomFormats/CustomFormatResource.cs index bfa9563b6..c88b034b0 100644 --- a/src/Radarr.Api.V3/CustomFormats/CustomFormatResource.cs +++ b/src/Radarr.Api.V3/CustomFormats/CustomFormatResource.cs @@ -13,26 +13,32 @@ namespace Radarr.Api.V3.CustomFormats [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public override int Id { get; set; } public string Name { get; set; } - public bool IncludeCustomFormatWhenRenaming { get; set; } + public bool? IncludeCustomFormatWhenRenaming { get; set; } public List Specifications { get; set; } } public static class CustomFormatResourceMapper { - public static CustomFormatResource ToResource(this CustomFormat model) + public static CustomFormatResource ToResource(this CustomFormat model, bool includeDetails) { - return new CustomFormatResource + var resource = new CustomFormatResource { Id = model.Id, - Name = model.Name, - IncludeCustomFormatWhenRenaming = model.IncludeCustomFormatWhenRenaming, - Specifications = model.Specifications.Select(x => x.ToSchema()).ToList() + Name = model.Name }; + + if (includeDetails) + { + resource.IncludeCustomFormatWhenRenaming = model.IncludeCustomFormatWhenRenaming; + resource.Specifications = model.Specifications.Select(x => x.ToSchema()).ToList(); + } + + return resource; } - public static List ToResource(this IEnumerable models) + public static List ToResource(this IEnumerable models, bool includeDetails) { - return models.Select(m => m.ToResource()).ToList(); + return models.Select(m => m.ToResource(includeDetails)).ToList(); } public static CustomFormat ToModel(this CustomFormatResource resource, List specifications) @@ -41,14 +47,15 @@ namespace Radarr.Api.V3.CustomFormats { Id = resource.Id, Name = resource.Name, - IncludeCustomFormatWhenRenaming = resource.IncludeCustomFormatWhenRenaming, - Specifications = resource.Specifications.Select(x => MapSpecification(x, specifications)).ToList() + IncludeCustomFormatWhenRenaming = resource.IncludeCustomFormatWhenRenaming ?? false, + Specifications = resource.Specifications?.Select(x => MapSpecification(x, specifications)).ToList() ?? new List() }; } private static ICustomFormatSpecification MapSpecification(CustomFormatSpecificationSchema resource, List specifications) { - var matchingSpec = specifications.SingleOrDefault(x => x.GetType().Name == resource.Implementation); + var matchingSpec = + specifications.SingleOrDefault(x => x.GetType().Name == resource.Implementation); if (matchingSpec is null) { @@ -57,6 +64,7 @@ namespace Radarr.Api.V3.CustomFormats } var type = matchingSpec.GetType(); + var spec = (ICustomFormatSpecification)SchemaBuilder.ReadFromSchema(resource.Fields, type); spec.Name = resource.Name; spec.Negate = resource.Negate; diff --git a/src/Radarr.Api.V3/History/HistoryResource.cs b/src/Radarr.Api.V3/History/HistoryResource.cs index 9f744dc44..3b617f243 100644 --- a/src/Radarr.Api.V3/History/HistoryResource.cs +++ b/src/Radarr.Api.V3/History/HistoryResource.cs @@ -17,6 +17,7 @@ namespace Radarr.Api.V3.History public List Languages { get; set; } public QualityModel Quality { get; set; } public List CustomFormats { get; set; } + public int CustomFormatScore { get; set; } public bool QualityCutoffNotMet { get; set; } public DateTime Date { get; set; } public string DownloadId { get; set; } @@ -37,6 +38,9 @@ namespace Radarr.Api.V3.History return null; } + var customFormats = formatCalculator.ParseCustomFormat(model, model.Movie); + var customFormatScore = model.Movie.Profile.CalculateCustomFormatScore(customFormats); + return new HistoryResource { Id = model.Id, @@ -45,7 +49,8 @@ namespace Radarr.Api.V3.History SourceTitle = model.SourceTitle, Languages = model.Languages, Quality = model.Quality, - CustomFormats = formatCalculator.ParseCustomFormat(model).ToResource(), + CustomFormats = customFormats.ToResource(false), + CustomFormatScore = customFormatScore, // QualityCutoffNotMet Date = model.Date, diff --git a/src/Radarr.Api.V3/Indexers/ReleaseResource.cs b/src/Radarr.Api.V3/Indexers/ReleaseResource.cs index a6a0eeee4..d37478be7 100644 --- a/src/Radarr.Api.V3/Indexers/ReleaseResource.cs +++ b/src/Radarr.Api.V3/Indexers/ReleaseResource.cs @@ -73,7 +73,7 @@ namespace Radarr.Api.V3.Indexers { Guid = releaseInfo.Guid, Quality = parsedMovieInfo.Quality, - CustomFormats = remoteMovie.CustomFormats.ToResource(), + CustomFormats = remoteMovie.CustomFormats.ToResource(false), CustomFormatScore = remoteMovie.CustomFormatScore, // QualityWeight @@ -87,7 +87,7 @@ namespace Radarr.Api.V3.Indexers ReleaseHash = parsedMovieInfo.ReleaseHash, Title = releaseInfo.Title, MovieTitles = parsedMovieInfo.MovieTitles, - Languages = parsedMovieInfo.Languages, + Languages = remoteMovie.Languages, Approved = model.Approved, TemporarilyRejected = model.TemporarilyRejected, Rejected = model.Rejected, diff --git a/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs b/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs index b048370c8..ac2b86f57 100644 --- a/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs +++ b/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs @@ -5,6 +5,7 @@ using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Languages; using NzbDrone.Core.MediaFiles.MovieImport.Manual; using NzbDrone.Core.Qualities; +using Radarr.Api.V3.CustomFormats; using Radarr.Api.V3.Movies; using Radarr.Http.REST; @@ -23,6 +24,7 @@ namespace Radarr.Api.V3.ManualImport public string ReleaseGroup { get; set; } public int QualityWeight { get; set; } public string DownloadId { get; set; } + public List CustomFormats { get; set; } public IEnumerable Rejections { get; set; } } @@ -44,9 +46,10 @@ namespace Radarr.Api.V3.ManualImport Name = model.Name, Size = model.Size, Movie = model.Movie.ToResource(0), + ReleaseGroup = model.ReleaseGroup, Quality = model.Quality, Languages = model.Languages, - ReleaseGroup = model.ReleaseGroup, + CustomFormats = model.CustomFormats.ToResource(false), // QualityWeight DownloadId = model.DownloadId, diff --git a/src/Radarr.Api.V3/MovieFiles/MovieFileController.cs b/src/Radarr.Api.V3/MovieFiles/MovieFileController.cs index d4f5c88f6..b6310ab17 100644 --- a/src/Radarr.Api.V3/MovieFiles/MovieFileController.cs +++ b/src/Radarr.Api.V3/MovieFiles/MovieFileController.cs @@ -54,7 +54,7 @@ namespace Radarr.Api.V3.MovieFiles movieFile.Movie = movie; var resource = movieFile.ToResource(movie, _qualityUpgradableSpecification); - resource.CustomFormats = _formatCalculator.ParseCustomFormat(movieFile).ToResource(); + resource.CustomFormats = _formatCalculator.ParseCustomFormat(movieFile).ToResource(false); return resource; } @@ -78,7 +78,7 @@ namespace Radarr.Api.V3.MovieFiles var resource = file.ToResource(movie, _qualityUpgradableSpecification); file.Movie = movie; - resource.CustomFormats = _formatCalculator.ParseCustomFormat(file).ToResource(); + resource.CustomFormats = _formatCalculator.ParseCustomFormat(file).ToResource(false); return new List { resource }; } diff --git a/src/Radarr.Api.V3/Parse/ParseController.cs b/src/Radarr.Api.V3/Parse/ParseController.cs index 8236f325a..bc9444c7e 100644 --- a/src/Radarr.Api.V3/Parse/ParseController.cs +++ b/src/Radarr.Api.V3/Parse/ParseController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Download.Aggregation; using NzbDrone.Core.Parser; using Radarr.Api.V3.Movies; using Radarr.Http; @@ -13,11 +14,15 @@ namespace Radarr.Api.V3.Parse { private readonly IParsingService _parsingService; private readonly IConfigService _configService; + private readonly IRemoteMovieAggregationService _aggregationService; - public ParseController(IParsingService parsingService, IConfigService configService) + public ParseController(IParsingService parsingService, + IConfigService configService, + IRemoteMovieAggregationService aggregationService) { _parsingService = parsingService; _configService = configService; + _aggregationService = aggregationService; } [HttpGet] @@ -40,6 +45,8 @@ namespace Radarr.Api.V3.Parse var remoteMovie = _parsingService.Map(parsedMovieInfo, ""); + _aggregationService.Augment(remoteMovie.RemoteMovie); + if (remoteMovie != null) { return new ParseResource diff --git a/src/Radarr.Api.V3/Queue/QueueResource.cs b/src/Radarr.Api.V3/Queue/QueueResource.cs index 7d6666d20..0e083bf5c 100644 --- a/src/Radarr.Api.V3/Queue/QueueResource.cs +++ b/src/Radarr.Api.V3/Queue/QueueResource.cs @@ -52,7 +52,7 @@ namespace Radarr.Api.V3.Queue Movie = includeMovie && model.Movie != null ? model.Movie.ToResource(0) : null, Languages = model.Languages, Quality = model.Quality, - CustomFormats = model.RemoteMovie?.CustomFormats?.ToResource(), + CustomFormats = model.RemoteMovie?.CustomFormats?.ToResource(false), Size = model.Size, Title = model.Title, Sizeleft = model.Sizeleft,