From 9a1a320110b694391a871f2a090ad7fc6098b5cd Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 28 Apr 2022 17:24:21 -0700 Subject: [PATCH] Fixed: Not including Original Title/Filename during rename when episode identifiers are missing Closes #5003 --- .../OriginalTitleFixture.cs | 64 ++++++++++++++++++- .../Organizer/FileNameBuilder.cs | 47 ++++++++++---- 2 files changed, 97 insertions(+), 14 deletions(-) diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/OriginalTitleFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/OriginalTitleFixture.cs index ea1ee4dc6..285459883 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/OriginalTitleFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/OriginalTitleFixture.cs @@ -79,13 +79,71 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests } [Test] - public void should_include_current_filename_if_not_including_multiple_naming_tokens() + public void should_include_current_filename_if_not_including_season_and_episode_tokens_for_standard_series() { _episodeFile.RelativePath = "My Series - S15E06 - City Sushi"; - _namingConfig.StandardEpisodeFormat = "{Original Title}"; + _namingConfig.StandardEpisodeFormat = "{Original Title} {Quality Title}"; Subject.BuildFileName(new List { _episode }, _series, _episodeFile) - .Should().Be("My Series - S15E06 - City Sushi"); + .Should().Be("My Series - S15E06 - City Sushi HDTV-720p"); + } + + [Test] + public void should_include_current_filename_if_not_including_air_date_token_for_daily_series() + { + _series.SeriesType = SeriesTypes.Daily; + _episode.AirDate = "2022-04-28"; + _episodeFile.RelativePath = "My Series - 2022-04-28 - City Sushi"; + _namingConfig.DailyEpisodeFormat = "{Original Title} {Quality Title}"; + + Subject.BuildFileName(new List { _episode }, _series, _episodeFile) + .Should().Be("My Series - 2022-04-28 - City Sushi HDTV-720p"); + } + + [Test] + public void should_include_current_filename_if_not_including_absolute_episode_number_token_for_anime_series() + { + _series.SeriesType = SeriesTypes.Anime; + _episode.AbsoluteEpisodeNumber = 123; + _episodeFile.RelativePath = "My Series - 123 - City Sushi"; + _namingConfig.AnimeEpisodeFormat = "{Original Title} {Quality Title}"; + + Subject.BuildFileName(new List { _episode }, _series, _episodeFile) + .Should().Be("My Series - 123 - City Sushi HDTV-720p"); + } + + [Test] + public void should_not_include_current_filename_if_including_season_and_episode_tokens_for_standard_series() + { + _episodeFile.RelativePath = "My Series - S15E06 - City Sushi"; + _namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} {[Original Title]}"; + + Subject.BuildFileName(new List { _episode }, _series, _episodeFile) + .Should().Be("My Series - S15E06"); + } + + [Test] + public void should_not_include_current_filename_if_including_air_date_token_for_daily_series() + { + _series.SeriesType = SeriesTypes.Daily; + _episode.AirDate = "2022-04-28"; + _episodeFile.RelativePath = "My Series - 2022-04-28 - City Sushi"; + _namingConfig.DailyEpisodeFormat = "{Series Title} - {Air-Date} {[Original Title]}"; + + Subject.BuildFileName(new List { _episode }, _series, _episodeFile) + .Should().Be("My Series - 2022-04-28"); + } + + [Test] + public void should_not_include_current_filename_if_including_absolute_episode_number_token_for_anime_series() + { + _series.SeriesType = SeriesTypes.Anime; + _episode.AbsoluteEpisodeNumber = 123; + _episodeFile.RelativePath = "My Series - 123 - City Sushi"; + _namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:00} {[Original Title]}"; + + Subject.BuildFileName(new List { _episode }, _series, _episodeFile) + .Should().Be("My Series - 123"); } } } diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 1a42bc7cc..8fd33e0f3 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -40,6 +40,7 @@ namespace NzbDrone.Core.Organizer private readonly ICached _absoluteEpisodeFormatCache; private readonly ICached _requiresEpisodeTitleCache; private readonly ICached _requiresAbsoluteEpisodeNumberCache; + private readonly ICached _patternHasEpisodeIdentifierCache; private readonly Logger _logger; private static readonly Regex TitleRegex = new Regex(@"(?\{\{|\}\})|\{(?[- ._\[(]*)(?(?:[a-z0-9]+)(?:(?[- ._]+)(?:[a-z0-9]+))?)(?::(?[a-z0-9+-]+(?[- ._)\]]*)\}", @@ -97,6 +98,7 @@ namespace NzbDrone.Core.Organizer _absoluteEpisodeFormatCache = cacheManager.GetCache(GetType(), "absoluteEpisodeFormat"); _requiresEpisodeTitleCache = cacheManager.GetCache(GetType(), "requiresEpisodeTitle"); _requiresAbsoluteEpisodeNumberCache = cacheManager.GetCache(GetType(), "requiresAbsoluteEpisodeNumber"); + _patternHasEpisodeIdentifierCache = cacheManager.GetCache(GetType(), "patternHasEpisodeIdentifier"); _logger = logger; } @@ -109,7 +111,7 @@ namespace NzbDrone.Core.Organizer if (!namingConfig.RenameEpisodes) { - return GetOriginalTitle(episodeFile, false) + extension; + return GetOriginalTitle(episodeFile, true) + extension; } if (namingConfig.StandardEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Standard) @@ -148,7 +150,7 @@ namespace NzbDrone.Core.Organizer { var splitPattern = splitPatterns[i]; var tokenHandlers = new Dictionary>(FileNameBuilderTokenEqualityComparer.Instance); - var multipleTokens = TitleRegex.Matches(splitPattern).Count > 1; + var patternHasEpisodeIdentifier = GetPatternHasEpisodeIdentifier(splitPattern); splitPattern = AddSeasonEpisodeNumberingTokens(splitPattern, tokenHandlers, episodes, namingConfig); splitPattern = AddAbsoluteNumberingTokens(splitPattern, tokenHandlers, series, episodes, namingConfig); @@ -159,7 +161,7 @@ namespace NzbDrone.Core.Organizer AddIdTokens(tokenHandlers, series); AddEpisodeTokens(tokenHandlers, episodes); AddEpisodeTitlePlaceholderTokens(tokenHandlers); - AddEpisodeFileTokens(tokenHandlers, episodeFile, multipleTokens); + AddEpisodeFileTokens(tokenHandlers, episodeFile, !patternHasEpisodeIdentifier); AddQualityTokens(tokenHandlers, series, episodeFile); AddMediaInfoTokens(tokenHandlers, episodeFile); AddPreferredWords(tokenHandlers, series, episodeFile, preferredWords); @@ -585,10 +587,10 @@ namespace NzbDrone.Core.Organizer tokenHandlers["{Episode CleanTitle}"] = m => GetEpisodeTitle(GetEpisodeTitles(episodes).Select(CleanTitle).ToList(), "and", maxLength); } - private void AddEpisodeFileTokens(Dictionary> tokenHandlers, EpisodeFile episodeFile, bool multipleTokens) + private void AddEpisodeFileTokens(Dictionary> tokenHandlers, EpisodeFile episodeFile, bool useCurrentFilenameAsFallback) { - tokenHandlers["{Original Title}"] = m => GetOriginalTitle(episodeFile, multipleTokens); - tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile, multipleTokens); + tokenHandlers["{Original Title}"] = m => GetOriginalTitle(episodeFile, useCurrentFilenameAsFallback); + tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile, useCurrentFilenameAsFallback); tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Sonarr"); } @@ -931,7 +933,7 @@ namespace NzbDrone.Core.Organizer private AbsoluteEpisodeFormat[] GetAbsoluteFormat(string pattern) { - return _absoluteEpisodeFormatCache.Get(pattern, () => AbsoluteEpisodePatternRegex.Matches(pattern).OfType() + return _absoluteEpisodeFormatCache.Get(pattern, () => AbsoluteEpisodePatternRegex.Matches(pattern).OfType() .Select(match => new AbsoluteEpisodeFormat { Separator = match.Groups["separator"].Value.IsNotNullOrWhiteSpace() ? match.Groups["separator"].Value : "-", @@ -939,6 +941,29 @@ namespace NzbDrone.Core.Organizer }).ToArray()); } + private bool GetPatternHasEpisodeIdentifier(string pattern) + { + return _patternHasEpisodeIdentifierCache.Get(pattern, () => + { + if (SeasonEpisodePatternRegex.IsMatch(pattern)) + { + return true; + } + + if (AbsoluteEpisodePatternRegex.IsMatch(pattern)) + { + return true; + } + + if (AirDateRegex.IsMatch(pattern)) + { + return true; + } + + return false; + }); + } + private List GetEpisodeTitles(List episodes) { if (episodes.Count == 1) @@ -1032,19 +1057,19 @@ namespace NzbDrone.Core.Organizer return string.Empty; } - private string GetOriginalTitle(EpisodeFile episodeFile, bool multipleTokens) + private string GetOriginalTitle(EpisodeFile episodeFile, bool useCurrentFilenameAsFallback) { if (episodeFile.SceneName.IsNullOrWhiteSpace()) { - return GetOriginalFileName(episodeFile, multipleTokens); + return GetOriginalFileName(episodeFile, useCurrentFilenameAsFallback); } return episodeFile.SceneName; } - private string GetOriginalFileName(EpisodeFile episodeFile, bool multipleTokens) + private string GetOriginalFileName(EpisodeFile episodeFile, bool useCurrentFilenameAsFallback) { - if (multipleTokens) + if (!useCurrentFilenameAsFallback) { return string.Empty; }