Fixed: Not including Original Title/Filename during rename when episode identifiers are missing
Closes #5003
This commit is contained in:
parent
f6664b8b42
commit
9a1a320110
|
@ -79,13 +79,71 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[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";
|
_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)
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
private readonly ICached<AbsoluteEpisodeFormat[]> _absoluteEpisodeFormatCache;
|
private readonly ICached<AbsoluteEpisodeFormat[]> _absoluteEpisodeFormatCache;
|
||||||
private readonly ICached<bool> _requiresEpisodeTitleCache;
|
private readonly ICached<bool> _requiresEpisodeTitleCache;
|
||||||
private readonly ICached<bool> _requiresAbsoluteEpisodeNumberCache;
|
private readonly ICached<bool> _requiresAbsoluteEpisodeNumberCache;
|
||||||
|
private readonly ICached<bool> _patternHasEpisodeIdentifierCache;
|
||||||
private readonly Logger _logger;
|
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>[- ._)\]]*)\}",
|
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");
|
_absoluteEpisodeFormatCache = cacheManager.GetCache<AbsoluteEpisodeFormat[]>(GetType(), "absoluteEpisodeFormat");
|
||||||
_requiresEpisodeTitleCache = cacheManager.GetCache<bool>(GetType(), "requiresEpisodeTitle");
|
_requiresEpisodeTitleCache = cacheManager.GetCache<bool>(GetType(), "requiresEpisodeTitle");
|
||||||
_requiresAbsoluteEpisodeNumberCache = cacheManager.GetCache<bool>(GetType(), "requiresAbsoluteEpisodeNumber");
|
_requiresAbsoluteEpisodeNumberCache = cacheManager.GetCache<bool>(GetType(), "requiresAbsoluteEpisodeNumber");
|
||||||
|
_patternHasEpisodeIdentifierCache = cacheManager.GetCache<bool>(GetType(), "patternHasEpisodeIdentifier");
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +111,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
|
|
||||||
if (!namingConfig.RenameEpisodes)
|
if (!namingConfig.RenameEpisodes)
|
||||||
{
|
{
|
||||||
return GetOriginalTitle(episodeFile, false) + extension;
|
return GetOriginalTitle(episodeFile, true) + extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (namingConfig.StandardEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Standard)
|
if (namingConfig.StandardEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Standard)
|
||||||
|
@ -148,7 +150,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
var splitPattern = splitPatterns[i];
|
var splitPattern = splitPatterns[i];
|
||||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
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 = AddSeasonEpisodeNumberingTokens(splitPattern, tokenHandlers, episodes, namingConfig);
|
||||||
splitPattern = AddAbsoluteNumberingTokens(splitPattern, tokenHandlers, series, episodes, namingConfig);
|
splitPattern = AddAbsoluteNumberingTokens(splitPattern, tokenHandlers, series, episodes, namingConfig);
|
||||||
|
@ -159,7 +161,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
AddIdTokens(tokenHandlers, series);
|
AddIdTokens(tokenHandlers, series);
|
||||||
AddEpisodeTokens(tokenHandlers, episodes);
|
AddEpisodeTokens(tokenHandlers, episodes);
|
||||||
AddEpisodeTitlePlaceholderTokens(tokenHandlers);
|
AddEpisodeTitlePlaceholderTokens(tokenHandlers);
|
||||||
AddEpisodeFileTokens(tokenHandlers, episodeFile, multipleTokens);
|
AddEpisodeFileTokens(tokenHandlers, episodeFile, !patternHasEpisodeIdentifier);
|
||||||
AddQualityTokens(tokenHandlers, series, episodeFile);
|
AddQualityTokens(tokenHandlers, series, episodeFile);
|
||||||
AddMediaInfoTokens(tokenHandlers, episodeFile);
|
AddMediaInfoTokens(tokenHandlers, episodeFile);
|
||||||
AddPreferredWords(tokenHandlers, series, episodeFile, preferredWords);
|
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);
|
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 Title}"] = m => GetOriginalTitle(episodeFile, useCurrentFilenameAsFallback);
|
||||||
tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile, multipleTokens);
|
tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile, useCurrentFilenameAsFallback);
|
||||||
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Sonarr");
|
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Sonarr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -939,6 +941,29 @@ namespace NzbDrone.Core.Organizer
|
||||||
}).ToArray());
|
}).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)
|
private List<string> GetEpisodeTitles(List<Episode> episodes)
|
||||||
{
|
{
|
||||||
if (episodes.Count == 1)
|
if (episodes.Count == 1)
|
||||||
|
@ -1032,19 +1057,19 @@ namespace NzbDrone.Core.Organizer
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetOriginalTitle(EpisodeFile episodeFile, bool multipleTokens)
|
private string GetOriginalTitle(EpisodeFile episodeFile, bool useCurrentFilenameAsFallback)
|
||||||
{
|
{
|
||||||
if (episodeFile.SceneName.IsNullOrWhiteSpace())
|
if (episodeFile.SceneName.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
return GetOriginalFileName(episodeFile, multipleTokens);
|
return GetOriginalFileName(episodeFile, useCurrentFilenameAsFallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
return episodeFile.SceneName;
|
return episodeFile.SceneName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetOriginalFileName(EpisodeFile episodeFile, bool multipleTokens)
|
private string GetOriginalFileName(EpisodeFile episodeFile, bool useCurrentFilenameAsFallback)
|
||||||
{
|
{
|
||||||
if (multipleTokens)
|
if (!useCurrentFilenameAsFallback)
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue