diff --git a/frontend/src/InteractiveImport/Language/SelectLanguageModalContentConnector.js b/frontend/src/InteractiveImport/Language/SelectLanguageModalContentConnector.js index 1eda0b176..f4b6796a3 100644 --- a/frontend/src/InteractiveImport/Language/SelectLanguageModalContentConnector.js +++ b/frontend/src/InteractiveImport/Language/SelectLanguageModalContentConnector.js @@ -11,7 +11,22 @@ function createMapStateToProps() { return createSelector( createLanguagesSelector(), (languages) => { - return languages; + const { + isFetching, + isPopulated, + error, + items + } = languages; + + const filterItems = ['Any', 'Original']; + const filteredLanguages = items.filter((lang) => !filterItems.includes(lang.name)); + + return { + isFetching, + isPopulated, + error, + items: filteredLanguages + }; } ); } diff --git a/frontend/src/Series/Index/Menus/SeriesIndexSortMenu.js b/frontend/src/Series/Index/Menus/SeriesIndexSortMenu.js index 018a4c8eb..7ea5ea75b 100644 --- a/frontend/src/Series/Index/Menus/SeriesIndexSortMenu.js +++ b/frontend/src/Series/Index/Menus/SeriesIndexSortMenu.js @@ -46,6 +46,15 @@ function SeriesIndexSortMenu(props) { Network + + Original Language + + + {originalLanguage.name} + + ); + } + if (name === 'qualityProfileId') { return ( { + if (series.originalLanguage) { + acc.push({ + id: series.originalLanguage.name, + name: series.originalLanguage.name + }); + } + + return acc; + }, []); + + return languageList.sort(sortByName); + } + }, { name: 'releaseGroups', label: 'Release Groups', diff --git a/frontend/src/Store/Actions/seriesIndexActions.js b/frontend/src/Store/Actions/seriesIndexActions.js index 0e1c4d9e5..8cdadba95 100644 --- a/frontend/src/Store/Actions/seriesIndexActions.js +++ b/frontend/src/Store/Actions/seriesIndexActions.js @@ -95,6 +95,12 @@ export const defaultState = { isSortable: true, isVisible: false }, + { + name: 'originalLanguage', + label: 'Original Language', + isSortable: true, + isVisible: false + }, { name: 'added', label: 'Added', @@ -249,6 +255,12 @@ export const defaultState = { return statistics.seasonCount; }, + originalLanguage: function(item) { + const { originalLanguage = {} } = item; + + return originalLanguage.name; + }, + ratings: function(item) { const { ratings = {} } = item; diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguageFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguageFixture.cs index 833b76b42..2d13d6792 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguageFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguageFixture.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators public class AggregateLanguageFixture : CoreTest { private LocalEpisode _localEpisode; + private Series _series; private string _simpleReleaseTitle = "Series.Title.S01E01.xyz-RlsGroup"; [SetUp] @@ -26,11 +27,16 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators var episodes = Builder.CreateListOfSize(1) .BuildList(); + _series = Builder.CreateNew() + .With(m => m.OriginalLanguage = Language.English) + .Build(); + _localEpisode = Builder.CreateNew() .With(l => l.DownloadClientEpisodeInfo = null) .With(l => l.FolderEpisodeInfo = null) .With(l => l.FileEpisodeInfo = null) .With(l => l.Episodes = episodes) + .With(l => l.Series = _series) .Build(); } @@ -72,7 +78,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators { var result = Subject.Aggregate(_localEpisode, null); - result.Languages.Should().Contain(Language.English); + result.Languages.Should().Contain(_series.OriginalLanguage); } [Test] @@ -120,13 +126,13 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators [Test] public void should_return_file_language_when_file_language_is_higher_than_others() { - _localEpisode.DownloadClientEpisodeInfo = GetParsedEpisodeInfo(new List { Language.English }, _simpleReleaseTitle); - _localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(new List { Language.English }, _simpleReleaseTitle); + _localEpisode.DownloadClientEpisodeInfo = GetParsedEpisodeInfo(new List { Language.Unknown }, _simpleReleaseTitle); + _localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(new List { Language.Unknown }, _simpleReleaseTitle); _localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(new List { Language.French }, _simpleReleaseTitle); GivenAugmenters(new List { Language.French }, - new List { Language.English }, - new List { Language.English }, + new List { Language.Unknown }, + new List { Language.Unknown }, null); Subject.Aggregate(_localEpisode, null).Languages.Should().Contain(_localEpisode.FileEpisodeInfo.Languages); @@ -135,9 +141,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators [Test] public void should_return_multi_language() { - GivenAugmenters(new List { Language.English }, + GivenAugmenters(new List { Language.Unknown }, new List { Language.French, Language.German }, - new List { Language.English }, + new List { Language.Unknown }, null); Subject.Aggregate(_localEpisode, null).Languages.Should().Equal(new List { Language.French, Language.German }); diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs index 1bc867448..0cb595dd0 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs @@ -260,46 +260,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse(); } - [Test] - public void should_return_false_if_it_is_a_preferred_word_downgrade_and_equal_language_and_quality() - { - var lowFormat = new List { new CustomFormat("Bad Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 2 } }; - - CustomFormatsFixture.GivenCustomFormats(lowFormat.First()); - - _series.QualityProfile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(); - - Mocker.GetMock() - .Setup(s => s.DownloadPropersAndRepacks) - .Returns(ProperDownloadTypes.DoNotPrefer); - - Mocker.GetMock() - .Setup(s => s.ParseCustomFormat(It.IsAny())) - .Returns(new List()); - - Mocker.GetMock() - .Setup(s => s.ParseCustomFormat(It.IsAny())) - .Returns(lowFormat); - - _localEpisode.Quality = new QualityModel(Quality.Bluray1080p); - - _localEpisode.Episodes = Builder.CreateListOfSize(1) - .All() - .With(e => e.EpisodeFileId = 1) - .With(e => e.EpisodeFile = new LazyLoaded( - new EpisodeFile - { - Quality = new QualityModel(Quality.Bluray1080p), - Languages = new List { Language.Spanish } - })) - .Build() - .ToList(); - - _localEpisode.FileEpisodeInfo = Builder.CreateNew().Build(); - - Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse(); - } - [Test] public void should_return_true_if_it_is_a_preferred_word_downgrade_and_language_downgrade_and_a_quality_upgrade() { diff --git a/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs index 40baa32ad..47c50014d 100644 --- a/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs @@ -11,21 +11,27 @@ namespace NzbDrone.Core.Test.ParserTests public class LanguageParserFixture : CoreTest { [TestCase("Title.the.Series.2009.S01E14.English.HDTV.XviD-LOL")] + [TestCase("Series Title - S01E01 - Pilot.English.sub")] + [TestCase("Series Title - S01E01 - Pilot.english.sub")] + public void should_parse_language_english(string postTitle) + { + var result = LanguageParser.ParseLanguages(postTitle); + result.First().Should().Be(Language.English); + } + + [TestCase("Spanish Killroy was Here S02E02 Flodden 720p AMZN WEB-DL DDP5 1 H 264-NTb")] + [TestCase("Title.the.Spanish.Series.S02E02.1080p.WEB.H264-CAKES")] + [TestCase("Title.the.Spanish.Series.S02E06.Field.of.Cloth.of.Gold.1080p.AMZN.WEBRip.DDP5.1.x264-NTb")] [TestCase("Title.the.Series.2009.S01E14.Germany.HDTV.XviD-LOL")] [TestCase("Title.the.Series.2009.S01E14.HDTV.XviD-LOL")] [TestCase("Title.the.Italian.Series.S01E01.The.Family.720p.HDTV.x264-FTP")] [TestCase("Title.the.Italy.Series.S02E01.720p.HDTV.x264-TLA")] [TestCase("Series Title - S01E01 - Pilot.en.sub")] [TestCase("Series Title - S01E01 - Pilot.eng.sub")] - [TestCase("Series Title - S01E01 - Pilot.English.sub")] - [TestCase("Series Title - S01E01 - Pilot.english.sub")] - [TestCase("Spanish Killroy was Here S02E02 Flodden 720p AMZN WEB-DL DDP5 1 H 264-NTb")] - [TestCase("Title.the.Spanish.Series.S02E02.1080p.WEB.H264-CAKES")] - [TestCase("Title.the.Spanish.Series.S02E06.Field.of.Cloth.of.Gold.1080p.AMZN.WEBRip.DDP5.1.x264-NTb")] - public void should_parse_language_english(string postTitle) + public void should_parse_language_unknown(string postTitle) { var result = LanguageParser.ParseLanguages(postTitle); - result.First().Should().Be(Language.English); + result.First().Should().Be(Language.Unknown); } [TestCase("Series Title - S01E01 - Pilot.sub")] @@ -314,7 +320,7 @@ namespace NzbDrone.Core.Test.ParserTests public void should_not_parse_series_or_episode_title(string postTitle) { var result = LanguageParser.ParseLanguages(postTitle); - result.First().Name.Should().Be(Language.English.Name); + result.First().Name.Should().Be(Language.Unknown.Name); } } } diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs index e86092f6c..1dd3940c5 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Languages; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -41,7 +42,8 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests SeriesTitle = _series.Title, SeasonNumber = 1, EpisodeNumbers = new[] { 1 }, - AbsoluteEpisodeNumbers = new int[0] + AbsoluteEpisodeNumbers = new int[0], + Languages = new List { Language.English } }; _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs index 5249af1aa..d8c6fbb8d 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using FizzWare.NBuilder; @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Languages; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -41,7 +42,8 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests SeriesTitle = _series.Title, SeriesTitleInfo = new SeriesTitleInfo(), SeasonNumber = 1, - EpisodeNumbers = new[] { 1 } + EpisodeNumbers = new[] { 1 }, + Languages = new List { Language.English } }; _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs index 6b7546db0..b1b6eec29 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs @@ -84,7 +84,8 @@ namespace NzbDrone.Core.CustomFormats ExtraInfo = new Dictionary { { "Size", episodeFile.Size }, - { "Filename", Path.GetFileName(episodeFile.RelativePath) } + { "Filename", Path.GetFileName(episodeFile.RelativePath) }, + { "OriginalLanguage", episodeFile.Series.Value.OriginalLanguage } } }; diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/LanguageSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/LanguageSpecification.cs index 341fb4d62..42950a407 100644 --- a/src/NzbDrone.Core/CustomFormats/Specifications/LanguageSpecification.cs +++ b/src/NzbDrone.Core/CustomFormats/Specifications/LanguageSpecification.cs @@ -34,7 +34,11 @@ namespace NzbDrone.Core.CustomFormats protected override bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo) { - return episodeInfo?.Languages?.Contains((Language)Value) ?? false; + var comparedLanguage = episodeInfo != null && Value == Language.Original.Id && episodeInfo.ExtraInfo.ContainsKey("OriginalLanguage") + ? (Language)episodeInfo.ExtraInfo["OriginalLanguage"] + : (Language)Value; + + return episodeInfo?.Languages?.Contains(comparedLanguage) ?? false; } public override NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Datastore/Migration/176_original_language.cs b/src/NzbDrone.Core/Datastore/Migration/176_original_language.cs new file mode 100644 index 000000000..91250d26d --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/176_original_language.cs @@ -0,0 +1,16 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; +using NzbDrone.Core.Languages; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(176)] + public class original_language : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("Series") + .AddColumn("OriginalLanguage").AsInt32().WithDefaultValue((int)Language.English); + } + } +} diff --git a/src/NzbDrone.Core/Languages/Language.cs b/src/NzbDrone.Core/Languages/Language.cs index ab400fc01..2e6bb638f 100644 --- a/src/NzbDrone.Core/Languages/Language.cs +++ b/src/NzbDrone.Core/Languages/Language.cs @@ -102,6 +102,7 @@ namespace NzbDrone.Core.Languages public static Language Malayalam => new Language(29, "Malayalam"); public static Language Ukrainian => new Language(30, "Ukrainian"); public static Language Slovak => new Language(31, "Slovak"); + public static Language Original => new Language(-2, "Original"); public static List All { @@ -140,7 +141,8 @@ namespace NzbDrone.Core.Languages Bulgarian, Malayalam, Ukrainian, - Slovak + Slovak, + Original }; } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguage.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguage.cs index 06b96e87c..07c0dd34e 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguage.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateLanguage.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { - var languages = new List { Language.English }; + var languages = new List { localEpisode.Series?.OriginalLanguage ?? Language.Unknown }; var languagesConfidence = Confidence.Default; foreach (var augmentLanguage in _augmentLanguages) @@ -37,7 +37,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators if (augmentedLanguage?.Languages != null && augmentedLanguage.Languages.Count > 0 && - !(augmentedLanguage.Languages.Count == 1 && augmentedLanguage.Languages.Contains(Language.English)) && !(augmentedLanguage.Languages.Count == 1 && augmentedLanguage.Languages.Contains(Language.Unknown))) { languages = augmentedLanguage.Languages; diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromDownloadClientItem.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromDownloadClientItem.cs index 6ef6d5d60..906d2ff7f 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromDownloadClientItem.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromDownloadClientItem.cs @@ -23,7 +23,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment foreach (var episode in localEpisode.Episodes) { - var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title, false); + var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title); languages = languages.Except(episodeTitleLanguage).ToList(); } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFileName.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFileName.cs index 7bdb8c31d..04384a27a 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFileName.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFileName.cs @@ -21,7 +21,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment foreach (var episode in localEpisode.Episodes) { - var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title, false); + var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title); languages = languages.Except(episodeTitleLanguage).ToList(); } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFolder.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFolder.cs index 39c079f19..3ce9b431c 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFolder.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFolder.cs @@ -21,7 +21,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment foreach (var episode in localEpisode.Episodes) { - var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title, false); + var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title); languages = languages.Except(episodeTitleLanguage).ToList(); } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs index 6b6c50871..5ba365cd2 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs @@ -139,6 +139,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual var rootFolder = Path.GetDirectoryName(path); var series = _seriesService.GetSeries(seriesId); + var languageParse = LanguageParser.ParseLanguages(path); + + if (languageParse.Count <= 1 && languageParse.First() == Language.Unknown && series != null) + { + languageParse = new List { series.OriginalLanguage }; + _logger.Debug("Language couldn't be parsed from release, falling back to series original language: {0}", series.OriginalLanguage.Name); + } + if (episodeIds.Any()) { var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem; @@ -153,7 +161,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual localEpisode.ExistingFile = series.Path.IsParentPath(path); localEpisode.Size = _diskProvider.GetFileSize(path); localEpisode.ReleaseGroup = releaseGroup.IsNullOrWhiteSpace() ? Parser.Parser.ParseReleaseGroup(path) : releaseGroup; - localEpisode.Languages = (languages?.SingleOrDefault() ?? Language.Unknown) == Language.Unknown ? LanguageParser.ParseLanguages(path) : languages; + localEpisode.Languages = (languages?.SingleOrDefault() ?? Language.Unknown) == Language.Unknown ? languageParse : languages; localEpisode.Quality = quality.Quality == Quality.Unknown ? QualityParser.ParseQuality(path) : quality; return MapItem(_importDecisionMaker.GetDecision(localEpisode, downloadClientItem), rootFolder, downloadId, null); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs index ae7f5d9af..4f1ddaf0e 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs @@ -5,9 +5,7 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; -using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Profiles.Releases; using NzbDrone.Core.Qualities; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications @@ -43,7 +41,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications } var qualityCompare = qualityComparer.Compare(localEpisode.Quality.Quality, episodeFile.Quality.Quality); - var customFormatScore = GetCustomFormatScore(localEpisode); if (qualityCompare < 0) { @@ -62,46 +59,9 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications _logger.Debug("This file isn't a quality revision upgrade for all episodes. Skipping {0}", localEpisode.Path); return Decision.Reject("Not a quality revision upgrade for existing episode file(s)"); } - - var customFormats = _customFormatCalculationService.ParseCustomFormat(episodeFile); - var episodeFileCustomFormatScore = localEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(customFormats); - - if (qualityCompare == 0 && customFormatScore < episodeFileCustomFormatScore) - { - _logger.Debug("This file isn't a custom format upgrade for episode. Skipping {0}", localEpisode.Path); - return Decision.Reject("Not a custom format upgrade for existing episode file(s)"); - } } return Decision.Accept(); } - - private int GetCustomFormatScore(LocalEpisode localEpisode) - { - var series = localEpisode.Series; - var scores = new List(); - var fileFormats = new List(); - var folderFormats = new List(); - var clientFormats = new List(); - - if (localEpisode.FileEpisodeInfo != null) - { - fileFormats = _customFormatCalculationService.ParseCustomFormat(localEpisode.FileEpisodeInfo); - } - - if (localEpisode.FolderEpisodeInfo != null) - { - folderFormats = _customFormatCalculationService.ParseCustomFormat(localEpisode.FolderEpisodeInfo); - } - - if (localEpisode.DownloadClientEpisodeInfo != null) - { - clientFormats = _customFormatCalculationService.ParseCustomFormat(localEpisode.DownloadClientEpisodeInfo); - } - - var formats = fileFormats.Union(folderFormats.Union(clientFormats)).ToList(); - - return series.QualityProfile.Value.CalculateCustomFormatScore(formats); - } } } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ShowResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ShowResource.cs index 76fc0ecd9..4672241c5 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ShowResource.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ShowResource.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace NzbDrone.Core.MetadataSource.SkyHook.Resource { @@ -29,6 +29,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource public string Network { get; set; } public string ImdbId { get; set; } + public string OriginalLanguage { get; set; } public List Actors { get; set; } public List Genres { get; set; } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 331a93027..4d4448287 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -9,8 +9,10 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.DataAugmentation.DailySeries; using NzbDrone.Core.Exceptions; +using NzbDrone.Core.Languages; using NzbDrone.Core.MediaCover; using NzbDrone.Core.MetadataSource.SkyHook.Resource; +using NzbDrone.Core.Parser; using NzbDrone.Core.Tv; namespace NzbDrone.Core.MetadataSource.SkyHook @@ -158,6 +160,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook series.CleanTitle = Parser.Parser.CleanSeriesTitle(show.Title); series.SortTitle = SeriesTitleNormalizer.Normalize(show.Title, show.TvdbId); + series.OriginalLanguage = IsoLanguages.Find(show.OriginalLanguage.ToLower())?.Language ?? Language.English; + if (show.FirstAired != null) { series.FirstAired = DateTime.ParseExact(show.FirstAired, "yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); diff --git a/src/NzbDrone.Core/Parser/LanguageParser.cs b/src/NzbDrone.Core/Parser/LanguageParser.cs index 2549b6974..7e1e76f5d 100644 --- a/src/NzbDrone.Core/Parser/LanguageParser.cs +++ b/src/NzbDrone.Core/Parser/LanguageParser.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Parser private static readonly Regex SubtitleLanguageRegex = new Regex(".+?[-_. ](?[a-z]{2,3})([-_. ](?full|forced|foreign|default|cc|psdh|sdh))*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); - public static List ParseLanguages(string title, bool defaultToEnglish = true) + public static List ParseLanguages(string title) { foreach (var regex in CleanSeriesTitleRegex) { @@ -175,7 +175,7 @@ namespace NzbDrone.Core.Parser if (!languages.Any()) { - languages.Add(defaultToEnglish ? Language.English : Language.Unknown); + languages.Add(Language.Unknown); } return languages.DistinctBy(l => (int)l).ToList(); diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index bbdc76156..4deed93c4 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -6,6 +6,8 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Languages; +using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -182,6 +184,29 @@ namespace NzbDrone.Core.Parser { remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, remoteEpisode.MappedSeasonNumber, sceneSource, searchCriteria); } + + parsedEpisodeInfo.ExtraInfo["OriginalLanguage"] = series.OriginalLanguage; + } + + // Use series language as fallback if we could't parse a language (more accurate than just using English) + if (parsedEpisodeInfo.Languages.Count <= 1 && parsedEpisodeInfo.Languages.First() == Language.Unknown && series != null) + { + parsedEpisodeInfo.Languages = new List { series.OriginalLanguage }; + _logger.Debug("Language couldn't be parsed from release, fallback to series original language: {0}", series.OriginalLanguage.Name); + } + + if (parsedEpisodeInfo.Languages.Contains(Language.Original)) + { + parsedEpisodeInfo.Languages.Remove(Language.Original); + + if (series != null && !parsedEpisodeInfo.Languages.Contains(series.OriginalLanguage)) + { + parsedEpisodeInfo.Languages.Add(series.OriginalLanguage); + } + else + { + parsedEpisodeInfo.Languages.Add(Language.Unknown); + } } if (remoteEpisode.Episodes == null) diff --git a/src/NzbDrone.Core/Tv/RefreshSeriesService.cs b/src/NzbDrone.Core/Tv/RefreshSeriesService.cs index f34ae0c21..64467ddb1 100644 --- a/src/NzbDrone.Core/Tv/RefreshSeriesService.cs +++ b/src/NzbDrone.Core/Tv/RefreshSeriesService.cs @@ -91,6 +91,7 @@ namespace NzbDrone.Core.Tv series.ImdbId = seriesInfo.ImdbId; series.AirTime = seriesInfo.AirTime; series.Overview = seriesInfo.Overview; + series.OriginalLanguage = seriesInfo.OriginalLanguage; series.Status = seriesInfo.Status; series.CleanTitle = seriesInfo.CleanTitle; series.SortTitle = seriesInfo.SortTitle; diff --git a/src/NzbDrone.Core/Tv/Series.cs b/src/NzbDrone.Core/Tv/Series.cs index cf398cab2..b10ac3de5 100644 --- a/src/NzbDrone.Core/Tv/Series.cs +++ b/src/NzbDrone.Core/Tv/Series.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Languages; using NzbDrone.Core.Profiles.Qualities; namespace NzbDrone.Core.Tv @@ -15,6 +16,7 @@ namespace NzbDrone.Core.Tv Actors = new List(); Seasons = new List(); Tags = new HashSet(); + OriginalLanguage = Language.English; } public int TvdbId { get; set; } @@ -47,6 +49,7 @@ namespace NzbDrone.Core.Tv public DateTime Added { get; set; } public DateTime? FirstAired { get; set; } public LazyLoaded QualityProfile { get; set; } + public Language OriginalLanguage { get; set; } public List Seasons { get; set; } public HashSet Tags { get; set; } diff --git a/src/Sonarr.Api.V3/Series/SeriesResource.cs b/src/Sonarr.Api.V3/Series/SeriesResource.cs index f7efc9f37..e5e672a0b 100644 --- a/src/Sonarr.Api.V3/Series/SeriesResource.cs +++ b/src/Sonarr.Api.V3/Series/SeriesResource.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using NzbDrone.Core.Languages; using NzbDrone.Core.MediaCover; using NzbDrone.Core.Tv; using Sonarr.Http.REST; @@ -30,7 +31,7 @@ namespace Sonarr.Api.V3.Series public string Network { get; set; } public string AirTime { get; set; } public List Images { get; set; } - + public Language OriginalLanguage { get; set; } public string RemotePoster { get; set; } public List Seasons { get; set; } public int Year { get; set; } @@ -100,6 +101,7 @@ namespace Sonarr.Api.V3.Series Seasons = model.Seasons.ToResource(includeSeasonImages), Year = model.Year, + OriginalLanguage = model.OriginalLanguage, Path = model.Path, QualityProfileId = model.QualityProfileId, @@ -161,6 +163,7 @@ namespace Sonarr.Api.V3.Series Seasons = resource.Seasons?.ToModel() ?? new List(), Year = resource.Year, + OriginalLanguage = resource.OriginalLanguage, Path = resource.Path, QualityProfileId = resource.QualityProfileId,