diff --git a/src/NzbDrone.Core.Test/Extras/Subtitles/SubtitleServiceFixture.cs b/src/NzbDrone.Core.Test/Extras/Subtitles/SubtitleServiceFixture.cs index e16c3e025..f74b9736f 100644 --- a/src/NzbDrone.Core.Test/Extras/Subtitles/SubtitleServiceFixture.cs +++ b/src/NzbDrone.Core.Test/Extras/Subtitles/SubtitleServiceFixture.cs @@ -74,7 +74,7 @@ public void should_not_import_non_subtitle_file(string filePath) [TestCase("Movie Title - 2022.srt", "Movie Title - 2022.srt")] [TestCase("Movie.Title.2022.en.srt", "Movie Title - 2022.en.srt")] [TestCase("Movie.Title.2022.english.srt", "Movie Title - 2022.en.srt")] - [TestCase("Movie Title 2022_en_sdh_forced.srt", "Movie Title - 2022.en.srt")] + [TestCase("Movie Title 2022_en_sdh_forced.srt", "Movie Title - 2022.en.sdh.forced.srt")] [TestCase("Movie_Title_2022 en.srt", "Movie Title - 2022.en.srt")] [TestCase(@"Subs\Movie.Title.2022\2_en.srt", "Movie Title - 2022.en.srt")] [TestCase("sub.srt", "Movie Title - 2022.srt")] @@ -95,7 +95,7 @@ public void should_import_multiple_subtitle_files_per_language() var files = new List { Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic(), - Path.Combine(_releaseFolder, "Movie.Title.2022.english.srt").AsOsAgnostic(), + Path.Combine(_releaseFolder, "Movie.Title.2022.eng.srt").AsOsAgnostic(), Path.Combine(_releaseFolder, "Subs", "Movie_Title_2022_en_forced.srt").AsOsAgnostic(), Path.Combine(_releaseFolder, "Subs", "Movie.Title.2022", "2_fr.srt").AsOsAgnostic() }; @@ -104,7 +104,7 @@ public void should_import_multiple_subtitle_files_per_language() { "Movie Title - 2022.1.en.srt", "Movie Title - 2022.2.en.srt", - "Movie Title - 2022.3.en.srt", + "Movie Title - 2022.en.forced.srt", "Movie Title - 2022.fr.srt", }; diff --git a/src/NzbDrone.Core/Datastore/Migration/214_add_language_tags_to_subtitle_files.cs b/src/NzbDrone.Core/Datastore/Migration/214_add_language_tags_to_subtitle_files.cs new file mode 100644 index 000000000..f545e589b --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/214_add_language_tags_to_subtitle_files.cs @@ -0,0 +1,14 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(214)] + public class add_language_tags_to_subtitle_files : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("SubtitleFiles").AddColumn("LanguageTags").AsString().Nullable(); + } + } +} diff --git a/src/NzbDrone.Core/Extras/Subtitles/SubtitleFile.cs b/src/NzbDrone.Core/Extras/Subtitles/SubtitleFile.cs index 7263b4c64..2b4ffe2d6 100644 --- a/src/NzbDrone.Core/Extras/Subtitles/SubtitleFile.cs +++ b/src/NzbDrone.Core/Extras/Subtitles/SubtitleFile.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using NzbDrone.Core.Extras.Files; using NzbDrone.Core.Languages; @@ -5,6 +6,17 @@ namespace NzbDrone.Core.Extras.Subtitles { public class SubtitleFile : ExtraFile { + public SubtitleFile() + { + LanguageTags = new List(); + } + public Language Language { get; set; } + + public string AggregateString => Language + LanguageTagsAsString + Extension; + + public List LanguageTags { get; set; } + + private string LanguageTagsAsString => string.Join(".", LanguageTags); } } diff --git a/src/NzbDrone.Core/Extras/Subtitles/SubtitleService.cs b/src/NzbDrone.Core/Extras/Subtitles/SubtitleService.cs index b45902711..f9836c5dd 100644 --- a/src/NzbDrone.Core/Extras/Subtitles/SubtitleService.cs +++ b/src/NzbDrone.Core/Extras/Subtitles/SubtitleService.cs @@ -72,7 +72,7 @@ public override IEnumerable MoveFilesAfterRename(Movie movie, List m.MovieFileId == movieFile.Id) - .GroupBy(s => s.Language + s.Extension).ToList(); + .GroupBy(s => s.AggregateString).ToList(); foreach (var group in groupedExtraFilesForMovieFile) { @@ -81,7 +81,7 @@ public override IEnumerable MoveFilesAfterRename(Movie movie, List 1); + var suffix = GetSuffix(subtitleFile.Language, copy, subtitleFile.LanguageTags, groupCount > 1); movedFiles.AddIfNotNull(MoveFile(movie, movieFile, subtitleFile, suffix)); copy++; @@ -116,7 +116,7 @@ public override IEnumerable ImportFiles(LocalMovie localMovie, MovieF try { // Filename match - if (Path.GetFileNameWithoutExtension(file).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)) + if (Path.GetFileNameWithoutExtension(file).StartsWithIgnoreCase(sourceFileName)) { matchingFiles.Add(file); continue; @@ -175,16 +175,24 @@ public override IEnumerable ImportFiles(LocalMovie localMovie, MovieF } } - var subtitleFiles = new List>(); + var subtitleFiles = new List(); foreach (string file in matchingFiles) { var language = LanguageParser.ParseSubtitleLanguage(file); var extension = Path.GetExtension(file); - subtitleFiles.Add(new Tuple(file, language, extension)); + var languageTags = LanguageParser.ParseLanguageTags(file); + var subFile = new SubtitleFile + { + Language = language, + Extension = extension + }; + subFile.LanguageTags = languageTags.ToList(); + subFile.RelativePath = PathExtensions.GetRelativePath(sourceFolder, file); + subtitleFiles.Add(subFile); } - var groupedSubtitleFiles = subtitleFiles.GroupBy(s => s.Item2 + s.Item3).ToList(); + var groupedSubtitleFiles = subtitleFiles.GroupBy(s => s.AggregateString).ToList(); foreach (var group in groupedSubtitleFiles) { @@ -193,14 +201,15 @@ public override IEnumerable ImportFiles(LocalMovie localMovie, MovieF foreach (var file in group) { + var path = Path.Combine(sourceFolder, file.RelativePath); + var language = file.Language; + var extension = file.Extension; + var suffix = GetSuffix(language, copy, file.LanguageTags, groupCount > 1); try { - var path = file.Item1; - var language = file.Item2; - var extension = file.Item3; - var suffix = GetSuffix(language, copy, groupCount > 1); var subtitleFile = ImportFile(localMovie.Movie, movieFile, path, isReadOnly, extension, suffix); subtitleFile.Language = language; + subtitleFile.LanguageTags = file.LanguageTags; _mediaFileAttributeService.SetFilePermissions(path); _subtitleFileService.Upsert(subtitleFile); @@ -211,7 +220,7 @@ public override IEnumerable ImportFiles(LocalMovie localMovie, MovieF } catch (Exception ex) { - _logger.Warn(ex, "Failed to import subtitle file: {0}", file.Item1); + _logger.Warn(ex, "Failed to import subtitle file: {0}", path); } } } @@ -219,7 +228,7 @@ public override IEnumerable ImportFiles(LocalMovie localMovie, MovieF return importedFiles; } - private string GetSuffix(Language language, int copy, bool multipleCopies = false) + private string GetSuffix(Language language, int copy, List languageTags, bool multipleCopies = false) { var suffixBuilder = new StringBuilder(); @@ -235,6 +244,12 @@ private string GetSuffix(Language language, int copy, bool multipleCopies = fals suffixBuilder.Append(IsoLanguages.Get(language).TwoLetterCode); } + if (languageTags.Any()) + { + suffixBuilder.Append("."); + suffixBuilder.Append(string.Join(".", languageTags)); + } + return suffixBuilder.ToString(); } } diff --git a/src/NzbDrone.Core/Parser/LanguageParser.cs b/src/NzbDrone.Core/Parser/LanguageParser.cs index 0296a3a26..fc222a670 100644 --- a/src/NzbDrone.Core/Parser/LanguageParser.cs +++ b/src/NzbDrone.Core/Parser/LanguageParser.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text.RegularExpressions; using NLog; +using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Languages; @@ -304,6 +305,25 @@ public static List ParseLanguages(string title) return languages.DistinctBy(l => (int)l).ToList(); } + public static IEnumerable ParseLanguageTags(string fileName) + { + try + { + var simpleFilename = Path.GetFileNameWithoutExtension(fileName); + var match = SubtitleLanguageRegex.Match(simpleFilename); + var languageTags = match.Groups["tags"].Captures.Cast() + .Where(tag => !tag.Value.Empty()) + .Select(tag => tag.Value.ToLower()); + return languageTags; + } + catch (Exception ex) + { + Logger.Debug(ex, "Failed parsing language tags from subtitle file: {0}", fileName); + } + + return Enumerable.Empty(); + } + public static Language ParseSubtitleLanguage(string fileName) { try