|
|
|
@ -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;
|
|
|
|
|
}
|
|
|
|
|