Fixed: Not including Original Title/Filename during rename when episode identifiers are missing

Closes #5003
This commit is contained in:
Mark McDowall 2022-04-28 17:24:21 -07:00
parent f6664b8b42
commit 9a1a320110
2 changed files with 97 additions and 14 deletions

View File

@ -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> { _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> { _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> { _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> { _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> { _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> { _episode }, _series, _episodeFile)
.Should().Be("My Series - 123");
}
}
}

View File

@ -40,6 +40,7 @@ namespace NzbDrone.Core.Organizer
private readonly ICached<AbsoluteEpisodeFormat[]> _absoluteEpisodeFormatCache;
private readonly ICached<bool> _requiresEpisodeTitleCache;
private readonly ICached<bool> _requiresAbsoluteEpisodeNumberCache;
private readonly ICached<bool> _patternHasEpisodeIdentifierCache;
private readonly Logger _logger;
private static readonly Regex TitleRegex = new Regex(@"(?<escaped>\{\{|\}\})|\{(?<prefix>[- ._\[(]*)(?<token>(?:[a-z0-9]+)(?:(?<separator>[- ._]+)(?:[a-z0-9]+))?)(?::(?<customFormat>[a-z0-9+-]+(?<!-)))?(?<suffix>[- ._)\]]*)\}",
@ -97,6 +98,7 @@ namespace NzbDrone.Core.Organizer
_absoluteEpisodeFormatCache = cacheManager.GetCache<AbsoluteEpisodeFormat[]>(GetType(), "absoluteEpisodeFormat");
_requiresEpisodeTitleCache = cacheManager.GetCache<bool>(GetType(), "requiresEpisodeTitle");
_requiresAbsoluteEpisodeNumberCache = cacheManager.GetCache<bool>(GetType(), "requiresAbsoluteEpisodeNumber");
_patternHasEpisodeIdentifierCache = cacheManager.GetCache<bool>(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<string, Func<TokenMatch, string>>(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<string, Func<TokenMatch, string>> tokenHandlers, EpisodeFile episodeFile, bool multipleTokens)
private void AddEpisodeFileTokens(Dictionary<string, Func<TokenMatch, string>> 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<Match>()
return _absoluteEpisodeFormatCache.Get(pattern, () => AbsoluteEpisodePatternRegex.Matches(pattern).OfType<Match>()
.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<string> GetEpisodeTitles(List<Episode> 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;
}