New: Ability to use Original Title only in naming settings
This commit is contained in:
parent
4026a0f096
commit
8711c41649
|
@ -327,7 +327,6 @@
|
||||||
<Content Include="Files\RSS\fanzub.xml">
|
<Content Include="Files\RSS\fanzub.xml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="License.txt" />
|
|
||||||
<None Include="..\NzbDrone.Test.Common\App.config">
|
<None Include="..\NzbDrone.Test.Common\App.config">
|
||||||
<Link>App.config</Link>
|
<Link>App.config</Link>
|
||||||
</None>
|
</None>
|
||||||
|
|
|
@ -384,7 +384,7 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_use_orginal_title()
|
public void should_be_able_to_use_original_title()
|
||||||
{
|
{
|
||||||
_series.Title = "30 Rock";
|
_series.Title = "30 Rock";
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - {Original Title}";
|
_namingConfig.StandardEpisodeFormat = "{Series Title} - {Original Title}";
|
||||||
|
@ -618,15 +618,16 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_use_empty_string_instead_of_null_when_scene_name_is_not_available()
|
public void should_use_existing_filename_when_scene_name_is_not_available()
|
||||||
{
|
{
|
||||||
_namingConfig.RenameEpisodes = true;
|
_namingConfig.RenameEpisodes = true;
|
||||||
_namingConfig.StandardEpisodeFormat = "{Original Title}";
|
_namingConfig.StandardEpisodeFormat = "{Original Title}";
|
||||||
|
|
||||||
_episodeFile.SceneName = null;
|
_episodeFile.SceneName = null;
|
||||||
|
_episodeFile.RelativePath = "existing.file.mkv";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
||||||
.Should().Be(String.Empty);
|
.Should().Be(Path.GetFileNameWithoutExtension(_episodeFile.RelativePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -640,6 +641,19 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
||||||
.Should().Be("South Park - S15E06 - S15E07 - (HDTV-720p, , DRONE) - City Sushi");
|
.Should().Be("South Park - S15E06 - S15E07 - (HDTV-720p, , DRONE) - City Sushi");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_be_able_to_use_only_original_title()
|
||||||
|
{
|
||||||
|
_series.Title = "30 Rock";
|
||||||
|
_namingConfig.StandardEpisodeFormat = "{Original Title}";
|
||||||
|
|
||||||
|
_episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL";
|
||||||
|
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
|
||||||
|
|
||||||
|
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
||||||
|
.Should().Be("30.Rock.S01E01.xvid-LOL");
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_allow_period_between_season_and_episode()
|
public void should_allow_period_between_season_and_episode()
|
||||||
{
|
{
|
||||||
|
|
|
@ -53,6 +53,9 @@ namespace NzbDrone.Core.Organizer
|
||||||
public static readonly Regex SeriesTitleRegex = new Regex(@"(?<token>\{(?:Series)(?<separator>[- ._])(Clean)?Title\})",
|
public static readonly Regex SeriesTitleRegex = new Regex(@"(?<token>\{(?:Series)(?<separator>[- ._])(Clean)?Title\})",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
private static readonly Regex OriginalTitleRegex = new Regex(@"(\^{original[- ._]title\}$)",
|
||||||
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
private static readonly Regex FileNameCleanupRegex = new Regex(@"\.{2,}", RegexOptions.Compiled);
|
private static readonly Regex FileNameCleanupRegex = new Regex(@"\.{2,}", RegexOptions.Compiled);
|
||||||
|
|
||||||
private static readonly char[] EpisodeTitleTrimCharacters = new[] { ' ', '.', '?' };
|
private static readonly char[] EpisodeTitleTrimCharacters = new[] { ' ', '.', '?' };
|
||||||
|
@ -78,27 +81,22 @@ namespace NzbDrone.Core.Organizer
|
||||||
|
|
||||||
if (!namingConfig.RenameEpisodes)
|
if (!namingConfig.RenameEpisodes)
|
||||||
{
|
{
|
||||||
if (episodeFile.SceneName.IsNullOrWhiteSpace())
|
return GetOriginalTitle(episodeFile);
|
||||||
{
|
|
||||||
if (episodeFile.RelativePath.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
return Path.GetFileNameWithoutExtension(episodeFile.Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Path.GetFileNameWithoutExtension(episodeFile.RelativePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return episodeFile.SceneName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (namingConfig.StandardEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Standard)
|
if (namingConfig.StandardEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Standard)
|
||||||
{
|
{
|
||||||
throw new NamingFormatException("Standard episode format cannot be null");
|
throw new NamingFormatException("Standard episode format cannot be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (namingConfig.DailyEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Daily)
|
if (namingConfig.DailyEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Daily)
|
||||||
{
|
{
|
||||||
throw new NamingFormatException("Daily episode format cannot be null");
|
throw new NamingFormatException("Daily episode format cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (namingConfig.AnimeEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Anime)
|
||||||
|
{
|
||||||
|
throw new NamingFormatException("Anime episode format cannot be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
var pattern = namingConfig.StandardEpisodeFormat;
|
var pattern = namingConfig.StandardEpisodeFormat;
|
||||||
|
@ -124,10 +122,10 @@ namespace NzbDrone.Core.Organizer
|
||||||
AddEpisodeFileTokens(tokenHandlers, series, episodeFile);
|
AddEpisodeFileTokens(tokenHandlers, series, episodeFile);
|
||||||
AddMediaInfoTokens(tokenHandlers, episodeFile);
|
AddMediaInfoTokens(tokenHandlers, episodeFile);
|
||||||
|
|
||||||
var filename = ReplaceTokens(pattern, tokenHandlers).Trim();
|
var fileName = ReplaceTokens(pattern, tokenHandlers).Trim();
|
||||||
filename = FileNameCleanupRegex.Replace(filename, match => match.Captures[0].Value[0].ToString());
|
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
|
||||||
|
|
||||||
return filename;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension)
|
public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension)
|
||||||
|
@ -398,7 +396,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
|
|
||||||
private void AddEpisodeFileTokens(Dictionary<String, Func<TokenMatch, String>> tokenHandlers, Series series, EpisodeFile episodeFile)
|
private void AddEpisodeFileTokens(Dictionary<String, Func<TokenMatch, String>> tokenHandlers, Series series, EpisodeFile episodeFile)
|
||||||
{
|
{
|
||||||
tokenHandlers["{Original Title}"] = m => episodeFile.SceneName ?? String.Empty;
|
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(episodeFile);
|
||||||
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? "DRONE";
|
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? "DRONE";
|
||||||
tokenHandlers["{Quality Title}"] = m => GetQualityTitle(series, episodeFile.Quality);
|
tokenHandlers["{Quality Title}"] = m => GetQualityTitle(series, episodeFile.Quality);
|
||||||
}
|
}
|
||||||
|
@ -556,17 +554,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return replacementText;
|
return replacementText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private sealed class TokenMatch
|
|
||||||
{
|
|
||||||
public Match RegexMatch { get; set; }
|
|
||||||
public String Prefix { get; set; }
|
|
||||||
public String Separator { get; set; }
|
|
||||||
public String Suffix { get; set; }
|
|
||||||
public String Token { get; set; }
|
|
||||||
public String CustomFormat { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ReplaceNumberTokens(string pattern, List<Episode> episodes)
|
private string ReplaceNumberTokens(string pattern, List<Episode> episodes)
|
||||||
{
|
{
|
||||||
var episodeIndex = 0;
|
var episodeIndex = 0;
|
||||||
|
@ -665,6 +652,31 @@ namespace NzbDrone.Core.Organizer
|
||||||
|
|
||||||
return _qualityDefinitionService.Get(quality.Quality).Title + qualitySuffix;
|
return _qualityDefinitionService.Get(quality.Quality).Title + qualitySuffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String GetOriginalTitle(EpisodeFile episodeFile)
|
||||||
|
{
|
||||||
|
if (episodeFile.SceneName.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
if (episodeFile.RelativePath.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return Path.GetFileNameWithoutExtension(episodeFile.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.GetFileNameWithoutExtension(episodeFile.RelativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return episodeFile.SceneName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class TokenMatch
|
||||||
|
{
|
||||||
|
public Match RegexMatch { get; set; }
|
||||||
|
public String Prefix { get; set; }
|
||||||
|
public String Separator { get; set; }
|
||||||
|
public String Suffix { get; set; }
|
||||||
|
public String Token { get; set; }
|
||||||
|
public String CustomFormat { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MultiEpisodeStyle
|
public enum MultiEpisodeStyle
|
||||||
|
|
|
@ -10,10 +10,13 @@ namespace NzbDrone.Core.Organizer
|
||||||
private static readonly Regex SeasonFolderRegex = new Regex(@"(\{season(\:\d+)?\})",
|
private static readonly Regex SeasonFolderRegex = new Regex(@"(\{season(\:\d+)?\})",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
internal static readonly Regex OriginalTitleRegex = new Regex(@"(\{original[- ._]title\})",
|
||||||
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
public static IRuleBuilderOptions<T, string> ValidEpisodeFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
public static IRuleBuilderOptions<T, string> ValidEpisodeFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||||
{
|
{
|
||||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.SeasonEpisodePatternRegex)).WithMessage("Must contain season and episode numbers");
|
return ruleBuilder.SetValidator(new ValidStandardEpisodeFormatValidator());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IRuleBuilderOptions<T, string> ValidDailyEpisodeFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
public static IRuleBuilderOptions<T, string> ValidDailyEpisodeFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||||
|
@ -41,10 +44,10 @@ namespace NzbDrone.Core.Organizer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ValidDailyEpisodeFormatValidator : PropertyValidator
|
public class ValidStandardEpisodeFormatValidator : PropertyValidator
|
||||||
{
|
{
|
||||||
public ValidDailyEpisodeFormatValidator()
|
public ValidStandardEpisodeFormatValidator()
|
||||||
: base("Must contain Air Date or Season and Episode")
|
: base("Must contain season and episode numbers OR Original Title")
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -54,7 +57,30 @@ namespace NzbDrone.Core.Organizer
|
||||||
var value = context.PropertyValue as String;
|
var value = context.PropertyValue as String;
|
||||||
|
|
||||||
if (!FileNameBuilder.SeasonEpisodePatternRegex.IsMatch(value) &&
|
if (!FileNameBuilder.SeasonEpisodePatternRegex.IsMatch(value) &&
|
||||||
!FileNameBuilder.AirDateRegex.IsMatch(value))
|
!FileNameValidation.OriginalTitleRegex.IsMatch(value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ValidDailyEpisodeFormatValidator : PropertyValidator
|
||||||
|
{
|
||||||
|
public ValidDailyEpisodeFormatValidator()
|
||||||
|
: base("Must contain Air Date OR Season and Episode OR Original Title")
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsValid(PropertyValidatorContext context)
|
||||||
|
{
|
||||||
|
var value = context.PropertyValue as String;
|
||||||
|
|
||||||
|
if (!FileNameBuilder.SeasonEpisodePatternRegex.IsMatch(value) &&
|
||||||
|
!FileNameBuilder.AirDateRegex.IsMatch(value) &&
|
||||||
|
!FileNameValidation.OriginalTitleRegex.IsMatch(value))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +92,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
public class ValidAnimeEpisodeFormatValidator : PropertyValidator
|
public class ValidAnimeEpisodeFormatValidator : PropertyValidator
|
||||||
{
|
{
|
||||||
public ValidAnimeEpisodeFormatValidator()
|
public ValidAnimeEpisodeFormatValidator()
|
||||||
: base("Must contain Absolute Episode number or Season and Episode")
|
: base("Must contain Absolute Episode number OR Season and Episode OR Original Title")
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -76,7 +102,8 @@ namespace NzbDrone.Core.Organizer
|
||||||
var value = context.PropertyValue as String;
|
var value = context.PropertyValue as String;
|
||||||
|
|
||||||
if (!FileNameBuilder.SeasonEpisodePatternRegex.IsMatch(value) &&
|
if (!FileNameBuilder.SeasonEpisodePatternRegex.IsMatch(value) &&
|
||||||
!FileNameBuilder.AbsoluteEpisodePatternRegex.IsMatch(value))
|
!FileNameBuilder.AbsoluteEpisodePatternRegex.IsMatch(value) &&
|
||||||
|
!FileNameValidation.OriginalTitleRegex.IsMatch(value))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
EpisodeNumber = 1,
|
EpisodeNumber = 1,
|
||||||
Title = "Episode Title (1)",
|
Title = "Episode Title (1)",
|
||||||
AirDate = "2013-10-30",
|
AirDate = "2013-10-30",
|
||||||
AbsoluteEpisodeNumber = 1
|
AbsoluteEpisodeNumber = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
_episode2 = new Episode
|
_episode2 = new Episode
|
||||||
|
@ -94,6 +94,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
Quality = new QualityModel(Quality.HDTV720p),
|
Quality = new QualityModel(Quality.HDTV720p),
|
||||||
RelativePath = "Series.Title.S01E01.720p.HDTV.x264-EVOLVE.mkv",
|
RelativePath = "Series.Title.S01E01.720p.HDTV.x264-EVOLVE.mkv",
|
||||||
|
SceneName = "Series.Title.S01E01.720p.HDTV.x264-EVOLVE",
|
||||||
ReleaseGroup = "RlsGrp",
|
ReleaseGroup = "RlsGrp",
|
||||||
MediaInfo = mediaInfo
|
MediaInfo = mediaInfo
|
||||||
};
|
};
|
||||||
|
@ -102,14 +103,16 @@ namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
Quality = new QualityModel(Quality.HDTV720p),
|
Quality = new QualityModel(Quality.HDTV720p),
|
||||||
RelativePath = "Series.Title.S01E01-E02.720p.HDTV.x264-EVOLVE.mkv",
|
RelativePath = "Series.Title.S01E01-E02.720p.HDTV.x264-EVOLVE.mkv",
|
||||||
|
SceneName = "Series.Title.S01E01-E02.720p.HDTV.x264-EVOLVE",
|
||||||
ReleaseGroup = "RlsGrp",
|
ReleaseGroup = "RlsGrp",
|
||||||
MediaInfo = mediaInfo
|
MediaInfo = mediaInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
_dailyEpisodeFile = new EpisodeFile
|
_dailyEpisodeFile = new EpisodeFile
|
||||||
{
|
{
|
||||||
Quality = new QualityModel(Quality.HDTV720p),
|
Quality = new QualityModel(Quality.HDTV720p),
|
||||||
RelativePath = "Series.Title.2013.10.30.HDTV.x264-EVOLVE.mkv",
|
RelativePath = "Series.Title.2013.10.30.HDTV.x264-EVOLVE.mkv",
|
||||||
|
SceneName = "Series.Title.2013.10.30.HDTV.x264-EVOLVE",
|
||||||
ReleaseGroup = "RlsGrp",
|
ReleaseGroup = "RlsGrp",
|
||||||
MediaInfo = mediaInfo
|
MediaInfo = mediaInfo
|
||||||
};
|
};
|
||||||
|
@ -118,6 +121,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
Quality = new QualityModel(Quality.HDTV720p),
|
Quality = new QualityModel(Quality.HDTV720p),
|
||||||
RelativePath = "Series.Title.001.HDTV.x264-EVOLVE.mkv",
|
RelativePath = "Series.Title.001.HDTV.x264-EVOLVE.mkv",
|
||||||
|
SceneName = "Series.Title.001.HDTV.x264-EVOLVE",
|
||||||
ReleaseGroup = "RlsGrp",
|
ReleaseGroup = "RlsGrp",
|
||||||
MediaInfo = mediaInfoAnime
|
MediaInfo = mediaInfoAnime
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue