diff --git a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguageFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguageFixture.cs index 49dca170e..addca3b46 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguageFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguageFixture.cs @@ -1,9 +1,14 @@ using System.Collections.Generic; +using System.Linq; using FizzWare.NBuilder; using FluentAssertions; +using FluentAssertions.Common; +using Moq; using NUnit.Framework; using NzbDrone.Core.Languages; using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators; +using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Language; +using NzbDrone.Core.Movies; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; @@ -13,17 +18,47 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators public class AggregateLanguageFixture : CoreTest { private LocalMovie _localMovie; + private Movie _movie; [SetUp] public void Setup() { + _movie = Builder.CreateNew() + .With(m => m.OriginalLanguage = Language.English) + .Build(); + _localMovie = Builder.CreateNew() .With(l => l.DownloadClientMovieInfo = null) .With(l => l.FolderMovieInfo = null) .With(l => l.FileMovieInfo = null) + .With(l => l.Movie = _movie) .Build(); } + private void GivenAugmenters(List fileNameLanguages, List folderNameLanguages, List clientLanguages, List mediaInfoLanguages) + { + var fileNameAugmenter = new Mock(); + var folderNameAugmenter = new Mock(); + var clientInfoAugmenter = new Mock(); + var mediaInfoAugmenter = new Mock(); + + fileNameAugmenter.Setup(s => s.AugmentLanguage(It.IsAny())) + .Returns(new AugmentLanguageResult(fileNameLanguages, Confidence.Filename)); + + folderNameAugmenter.Setup(s => s.AugmentLanguage(It.IsAny())) + .Returns(new AugmentLanguageResult(folderNameLanguages, Confidence.Foldername)); + + clientInfoAugmenter.Setup(s => s.AugmentLanguage(It.IsAny())) + .Returns(new AugmentLanguageResult(clientLanguages, Confidence.DownloadClientItem)); + + mediaInfoAugmenter.Setup(s => s.AugmentLanguage(It.IsAny())) + .Returns(new AugmentLanguageResult(mediaInfoLanguages, Confidence.MediaInfo)); + + var mocks = new List> { fileNameAugmenter, folderNameAugmenter, clientInfoAugmenter, mediaInfoAugmenter }; + + Mocker.SetConstant>(mocks.Select(c => c.Object)); + } + private ParsedMovieInfo GetParsedMovieInfo(List languages) { return new ParsedMovieInfo @@ -35,56 +70,77 @@ private ParsedMovieInfo GetParsedMovieInfo(List languages) [Test] public void should_return_default_if_no_info_is_known() { - Subject.Aggregate(_localMovie, false).Languages.Should().Contain(Language.Unknown); + var result = Subject.Aggregate(_localMovie, false); + + result.Languages.Should().Contain(_movie.OriginalLanguage); } [Test] public void should_return_file_language_when_only_file_info_is_known() { - _localMovie.FileMovieInfo = GetParsedMovieInfo(new List { Language.English }); + GivenAugmenters(new List { Language.French }, + null, + null, + null); - Subject.Aggregate(_localMovie, false).Languages.Should().Equal(_localMovie.FileMovieInfo.Languages); + Subject.Aggregate(_localMovie, false).Languages.Should().Equal(new List { Language.French }); } [Test] public void should_return_folder_language_when_folder_info_is_known() { - _localMovie.FolderMovieInfo = GetParsedMovieInfo(new List { Language.English }); - _localMovie.FileMovieInfo = GetParsedMovieInfo(new List { Language.English }); + GivenAugmenters(new List { Language.French }, + new List { Language.German }, + null, + null); var aggregation = Subject.Aggregate(_localMovie, false); - aggregation.Languages.Should().Equal(_localMovie.FolderMovieInfo.Languages); + aggregation.Languages.Should().Equal(new List { Language.German }); } [Test] public void should_return_download_client_item_language_when_download_client_item_info_is_known() { - _localMovie.DownloadClientMovieInfo = GetParsedMovieInfo(new List { Language.English }); - _localMovie.FolderMovieInfo = GetParsedMovieInfo(new List { Language.English }); - _localMovie.FileMovieInfo = GetParsedMovieInfo(new List { Language.English }); + GivenAugmenters(new List { Language.French }, + new List { Language.German }, + new List { Language.Spanish }, + null); - Subject.Aggregate(_localMovie, false).Languages.Should().Equal(_localMovie.DownloadClientMovieInfo.Languages); - } - - [Test] - public void should_return_file_language_when_file_language_is_higher_than_others() - { - _localMovie.DownloadClientMovieInfo = GetParsedMovieInfo(new List { Language.Unknown }); - _localMovie.FolderMovieInfo = GetParsedMovieInfo(new List { Language.Unknown }); - _localMovie.FileMovieInfo = GetParsedMovieInfo(new List { Language.French }); - - Subject.Aggregate(_localMovie, false).Languages.Should().Equal(_localMovie.FileMovieInfo.Languages); + Subject.Aggregate(_localMovie, false).Languages.Should().Equal(new List { Language.Spanish }); } [Test] public void should_return_multi_language() { - _localMovie.DownloadClientMovieInfo = GetParsedMovieInfo(new List { Language.Unknown }); - _localMovie.FolderMovieInfo = GetParsedMovieInfo(new List { Language.English, Language.German }); - _localMovie.FileMovieInfo = GetParsedMovieInfo(new List { Language.Unknown }); + GivenAugmenters(new List { Language.Unknown }, + new List { Language.French, Language.German }, + new List { Language.Unknown }, + null); - Subject.Aggregate(_localMovie, false).Languages.Should().Equal(_localMovie.FolderMovieInfo.Languages); + Subject.Aggregate(_localMovie, false).Languages.Should().Equal(new List { Language.French, Language.German }); + } + + [Test] + public void should_use_mediainfo_over_others() + { + GivenAugmenters(new List { Language.Unknown }, + new List { Language.French, Language.German }, + new List { Language.Unknown }, + new List { Language.Japanese, Language.English }); + + Subject.Aggregate(_localMovie, false).Languages.Should().Equal(new List { Language.Japanese, Language.English }); + } + + [Test] + public void should_not_use_mediainfo_if_unknown() + { + GivenAugmenters(new List { Language.Unknown }, + new List { Language.French, Language.German }, + new List { Language.Unknown }, + new List { Language.Unknown }); + + Subject.Aggregate(_localMovie, false).Languages.Should().Equal(new List { Language.French, Language.German }); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguage.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguage.cs index 80dd6c458..3a202cef5 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguage.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/AggregateLanguage.cs @@ -1,53 +1,43 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.Extensions; using NzbDrone.Core.Languages; +using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Language; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators { public class AggregateLanguage : IAggregateLocalMovie { + private readonly IEnumerable _augmentQualities; private readonly Logger _logger; - public AggregateLanguage(Logger logger) + public AggregateLanguage(IEnumerable augmentQualities, + Logger logger) { + _augmentQualities = augmentQualities; _logger = logger; } public LocalMovie Aggregate(LocalMovie localMovie, bool otherFiles) { - // Get languages in preferred order, download client item, folder and finally file. - // Non-English languages will be preferred later, in the event there is a conflict - // between parsed languages the more preferred item will be used. - var languages = new List(); + var augmentedLanguages = _augmentQualities.Select(a => a.AugmentLanguage(localMovie)) + .Where(a => a != null) + .OrderBy(a => a.Confidence); - languages.AddRange(localMovie.DownloadClientMovieInfo?.Languages ?? new List()); + var languages = new List { localMovie.Movie.OriginalLanguage ?? Language.Unknown }; + var languagesConfidence = Confidence.Default; - if (!languages.Any(l => l != Language.Unknown)) + foreach (var augmentedLanguage in augmentedLanguages) { - languages = localMovie.FolderMovieInfo?.Languages ?? new List(); + if (augmentedLanguage?.Languages != null && augmentedLanguage.Languages.Count > 0 && !(augmentedLanguage.Languages.Count == 1 && augmentedLanguage.Languages.Contains(Language.Unknown))) + { + languages = augmentedLanguage.Languages; + languagesConfidence = augmentedLanguage.Confidence; + } } - if (!languages.Any(l => l != Language.Unknown)) - { - languages = localMovie.FileMovieInfo?.Languages ?? new List(); - } - - if (!languages.Any()) - { - languages.Add(Language.Unknown); - } - - languages = languages.Distinct().ToList(); - - if (languages.Count == 1 && languages.Contains(Language.Unknown)) - { - languages = new List { localMovie.Movie.OriginalLanguage }; - } - - _logger.Debug("Using languages: {0}", languages.Select(l => l.Name).ToList().Join(",")); + _logger.Debug("Using languages: {0}", languages); localMovie.Languages = languages; diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromDownloadClientItem.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromDownloadClientItem.cs new file mode 100644 index 000000000..d65a01d02 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromDownloadClientItem.cs @@ -0,0 +1,19 @@ +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Language +{ + public class AugmentLanguageFromDownloadClientItem : IAugmentLanguage + { + public AugmentLanguageResult AugmentLanguage(LocalMovie localMovie) + { + var languages = localMovie.DownloadClientMovieInfo?.Languages; + + if (languages == null) + { + return null; + } + + return new AugmentLanguageResult(languages, Confidence.DownloadClientItem); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFileName.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFileName.cs new file mode 100644 index 000000000..f762cbf86 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFileName.cs @@ -0,0 +1,19 @@ +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Language +{ + public class AugmentLanguageFromFileName : IAugmentLanguage + { + public AugmentLanguageResult AugmentLanguage(LocalMovie localMovie) + { + var languages = localMovie.FileMovieInfo?.Languages; + + if (languages == null) + { + return null; + } + + return new AugmentLanguageResult(languages, Confidence.Filename); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFolder.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFolder.cs new file mode 100644 index 000000000..1261c7021 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromFolder.cs @@ -0,0 +1,19 @@ +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Language +{ + public class AugmentLanguageFromFolder : IAugmentLanguage + { + public AugmentLanguageResult AugmentLanguage(LocalMovie localMovie) + { + var languages = localMovie.FolderMovieInfo?.Languages; + + if (languages == null) + { + return null; + } + + return new AugmentLanguageResult(languages, Confidence.Foldername); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromMediaInfo.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromMediaInfo.cs new file mode 100644 index 000000000..55ec23021 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageFromMediaInfo.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Language +{ + public class AugmentLanguageFromMediaInfo : IAugmentLanguage + { + public AugmentLanguageResult AugmentLanguage(LocalMovie localMovie) + { + if (localMovie.MediaInfo == null) + { + return null; + } + + var audioLanguages = localMovie.MediaInfo.AudioLanguages.Split('/').Distinct().ToList(); + + var languages = new List(); + + foreach (var audioLanguage in audioLanguages) + { + var language = IsoLanguages.FindByName(audioLanguage)?.Language; + languages.AddIfNotNull(language); + } + + if (languages.Count == 0) + { + return null; + } + + return new AugmentLanguageResult(languages, Confidence.MediaInfo); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageResult.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageResult.cs new file mode 100644 index 000000000..a0faf2b62 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/AugmentLanguageResult.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Language +{ + public class AugmentLanguageResult + { + public List Languages { get; set; } + public Confidence Confidence { get; set; } + + public AugmentLanguageResult(List languages, + Confidence confidence) + { + Languages = languages; + Confidence = confidence; + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/Confidence.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/Confidence.cs new file mode 100644 index 000000000..6b3bb5983 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/Confidence.cs @@ -0,0 +1,11 @@ +namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Language +{ + public enum Confidence + { + Default, + Filename, + Foldername, + DownloadClientItem, + MediaInfo + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/IAugmentLanguage.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/IAugmentLanguage.cs new file mode 100644 index 000000000..79f06b909 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Aggregation/Aggregators/Augmenters/Language/IAugmentLanguage.cs @@ -0,0 +1,9 @@ +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Language +{ + public interface IAugmentLanguage + { + AugmentLanguageResult AugmentLanguage(LocalMovie localMovie); + } +} diff --git a/src/NzbDrone.Core/Parser/IsoLanguage.cs b/src/NzbDrone.Core/Parser/IsoLanguage.cs index aefbff9b6..6f919c572 100644 --- a/src/NzbDrone.Core/Parser/IsoLanguage.cs +++ b/src/NzbDrone.Core/Parser/IsoLanguage.cs @@ -7,13 +7,15 @@ public class IsoLanguage public string TwoLetterCode { get; set; } public string ThreeLetterCode { get; set; } public string CountryCode { get; set; } + public string EnglishName { get; set; } public Language Language { get; set; } - public IsoLanguage(string twoLetterCode, string countryCode, string threeLetterCode, Language language) + public IsoLanguage(string twoLetterCode, string countryCode, string threeLetterCode, string englishName, Language language) { TwoLetterCode = twoLetterCode; ThreeLetterCode = threeLetterCode; CountryCode = countryCode; + EnglishName = englishName; Language = language; } } diff --git a/src/NzbDrone.Core/Parser/IsoLanguages.cs b/src/NzbDrone.Core/Parser/IsoLanguages.cs index dc944dc96..18a4b6f4d 100644 --- a/src/NzbDrone.Core/Parser/IsoLanguages.cs +++ b/src/NzbDrone.Core/Parser/IsoLanguages.cs @@ -8,30 +8,30 @@ public static class IsoLanguages { private static readonly HashSet All = new HashSet { - new IsoLanguage("en", "", "eng", Language.English), - new IsoLanguage("fr", "", "fra", Language.French), - new IsoLanguage("es", "", "spa", Language.Spanish), - new IsoLanguage("de", "", "deu", Language.German), - new IsoLanguage("it", "", "ita", Language.Italian), - new IsoLanguage("da", "", "dan", Language.Danish), - new IsoLanguage("nl", "", "nld", Language.Dutch), - new IsoLanguage("ja", "", "jpn", Language.Japanese), - new IsoLanguage("is", "", "isl", Language.Icelandic), - new IsoLanguage("zh", "", "zho", Language.Chinese), - new IsoLanguage("ru", "", "rus", Language.Russian), - new IsoLanguage("pl", "", "pol", Language.Polish), - new IsoLanguage("vi", "", "vie", Language.Vietnamese), - new IsoLanguage("sv", "", "swe", Language.Swedish), - new IsoLanguage("no", "", "nor", Language.Norwegian), - new IsoLanguage("nb", "", "nob", Language.Norwegian), - new IsoLanguage("fi", "", "fin", Language.Finnish), - new IsoLanguage("tr", "", "tur", Language.Turkish), - new IsoLanguage("pt", "", "por", Language.Portuguese), - new IsoLanguage("el", "", "ell", Language.Greek), - new IsoLanguage("ko", "", "kor", Language.Korean), - new IsoLanguage("hu", "", "hun", Language.Hungarian), - new IsoLanguage("he", "", "heb", Language.Hebrew), - new IsoLanguage("cs", "", "ces", Language.Czech) + new IsoLanguage("en", "", "eng", "English", Language.English), + new IsoLanguage("fr", "", "fra", "French", Language.French), + new IsoLanguage("es", "", "spa", "Spanish", Language.Spanish), + new IsoLanguage("de", "", "deu", "German", Language.German), + new IsoLanguage("it", "", "ita", "Italian", Language.Italian), + new IsoLanguage("da", "", "dan", "Danish", Language.Danish), + new IsoLanguage("nl", "", "nld", "Dutch", Language.Dutch), + new IsoLanguage("ja", "", "jpn", "Japanese", Language.Japanese), + new IsoLanguage("is", "", "isl", "Icelandic", Language.Icelandic), + new IsoLanguage("zh", "", "zho", "Chinese", Language.Chinese), + new IsoLanguage("ru", "", "rus", "Russian", Language.Russian), + new IsoLanguage("pl", "", "pol", "Polish", Language.Polish), + new IsoLanguage("vi", "", "vie", "Vietnamese", Language.Vietnamese), + new IsoLanguage("sv", "", "swe", "Swedish", Language.Swedish), + new IsoLanguage("no", "", "nor", "Norwegian", Language.Norwegian), + new IsoLanguage("nb", "", "nob", "Norwegian Bokmal", Language.Norwegian), + new IsoLanguage("fi", "", "fin", "Finnish", Language.Finnish), + new IsoLanguage("tr", "", "tur", "Turkish", Language.Turkish), + new IsoLanguage("pt", "", "por", "Portuguese", Language.Portuguese), + new IsoLanguage("el", "", "ell", "Greek", Language.Greek), + new IsoLanguage("ko", "", "kor", "Korean", Language.Korean), + new IsoLanguage("hu", "", "hun", "Hungarian", Language.Hungarian), + new IsoLanguage("he", "", "heb", "Hebrew", Language.Hebrew), + new IsoLanguage("cs", "", "ces", "Czech", Language.Czech) }; public static IsoLanguage Find(string isoCode) @@ -62,6 +62,11 @@ public static IsoLanguage Find(string isoCode) return null; } + public static IsoLanguage FindByName(string name) + { + return All.FirstOrDefault(l => l.EnglishName == name.Trim()); + } + public static IsoLanguage Get(Language language) { return All.FirstOrDefault(l => l.Language == language);