mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-02-06 11:50:56 +02:00
Various naming fixes
New: Multi-episode style (Range) New: Example for mulit-episode anime naming Fixed: Validate anime episode format when saving
This commit is contained in:
parent
a83f49ca32
commit
cf9f5fe569
@ -82,6 +82,7 @@ namespace NzbDrone.Api.Config
|
|||||||
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
||||||
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
||||||
var animeEpisodeSampleResult = _filenameSampleService.GetAnimeSample(nameSpec);
|
var animeEpisodeSampleResult = _filenameSampleService.GetAnimeSample(nameSpec);
|
||||||
|
var animeMultiEpisodeSampleResult = _filenameSampleService.GetAnimeMultiEpisodeSample(nameSpec);
|
||||||
|
|
||||||
sampleResource.SingleEpisodeExample = _filenameValidationService.ValidateStandardFilename(singleEpisodeSampleResult) != null
|
sampleResource.SingleEpisodeExample = _filenameValidationService.ValidateStandardFilename(singleEpisodeSampleResult) != null
|
||||||
? "Invalid format"
|
? "Invalid format"
|
||||||
@ -99,6 +100,10 @@ namespace NzbDrone.Api.Config
|
|||||||
? "Invalid format"
|
? "Invalid format"
|
||||||
: animeEpisodeSampleResult.FileName;
|
: animeEpisodeSampleResult.FileName;
|
||||||
|
|
||||||
|
sampleResource.AnimeMultiEpisodeExample = _filenameValidationService.ValidateAnimeFilename(animeMultiEpisodeSampleResult) != null
|
||||||
|
? "Invalid format"
|
||||||
|
: animeMultiEpisodeSampleResult.FileName;
|
||||||
|
|
||||||
sampleResource.SeriesFolderExample = nameSpec.SeriesFolderFormat.IsNullOrWhiteSpace()
|
sampleResource.SeriesFolderExample = nameSpec.SeriesFolderFormat.IsNullOrWhiteSpace()
|
||||||
? "Invalid format"
|
? "Invalid format"
|
||||||
: _filenameSampleService.GetSeriesFolderSample(nameSpec);
|
: _filenameSampleService.GetSeriesFolderSample(nameSpec);
|
||||||
@ -115,26 +120,22 @@ namespace NzbDrone.Api.Config
|
|||||||
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
||||||
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
||||||
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
||||||
|
var animeEpisodeSampleResult = _filenameSampleService.GetAnimeSample(nameSpec);
|
||||||
|
var animeMultiEpisodeSampleResult = _filenameSampleService.GetAnimeMultiEpisodeSample(nameSpec);
|
||||||
|
|
||||||
var singleEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(singleEpisodeSampleResult);
|
var singleEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(singleEpisodeSampleResult);
|
||||||
var multiEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(multiEpisodeSampleResult);
|
var multiEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(multiEpisodeSampleResult);
|
||||||
var dailyEpisodeValidationResult = _filenameValidationService.ValidateDailyFilename(dailyEpisodeSampleResult);
|
var dailyEpisodeValidationResult = _filenameValidationService.ValidateDailyFilename(dailyEpisodeSampleResult);
|
||||||
|
var animeEpisodeValidationResult = _filenameValidationService.ValidateAnimeFilename(animeEpisodeSampleResult);
|
||||||
|
var animeMultiEpisodeValidationResult = _filenameValidationService.ValidateAnimeFilename(animeMultiEpisodeSampleResult);
|
||||||
|
|
||||||
var validationFailures = new List<ValidationFailure>();
|
var validationFailures = new List<ValidationFailure>();
|
||||||
|
|
||||||
if (singleEpisodeValidationResult != null)
|
validationFailures.AddIfNotNull(singleEpisodeValidationResult);
|
||||||
{
|
validationFailures.AddIfNotNull(multiEpisodeValidationResult);
|
||||||
validationFailures.Add(singleEpisodeValidationResult);
|
validationFailures.AddIfNotNull(dailyEpisodeValidationResult);
|
||||||
}
|
validationFailures.AddIfNotNull(animeEpisodeValidationResult);
|
||||||
|
validationFailures.AddIfNotNull(animeMultiEpisodeValidationResult);
|
||||||
if (multiEpisodeValidationResult != null)
|
|
||||||
{
|
|
||||||
validationFailures.Add(multiEpisodeValidationResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dailyEpisodeValidationResult != null)
|
|
||||||
{
|
|
||||||
validationFailures.Add(dailyEpisodeValidationResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (validationFailures.Any())
|
if (validationFailures.Any())
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
public string MultiEpisodeExample { get; set; }
|
public string MultiEpisodeExample { get; set; }
|
||||||
public string DailyEpisodeExample { get; set; }
|
public string DailyEpisodeExample { get; set; }
|
||||||
public string AnimeEpisodeExample { get; set; }
|
public string AnimeEpisodeExample { get; set; }
|
||||||
|
public string AnimeMultiEpisodeExample { get; set; }
|
||||||
public string SeriesFolderExample { get; set; }
|
public string SeriesFolderExample { get; set; }
|
||||||
public string SeasonFolderExample { get; set; }
|
public string SeasonFolderExample { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
|||||||
private Series _series;
|
private Series _series;
|
||||||
private Episode _episode1;
|
private Episode _episode1;
|
||||||
private Episode _episode2;
|
private Episode _episode2;
|
||||||
|
private Episode _episode3;
|
||||||
private EpisodeFile _episodeFile;
|
private EpisodeFile _episodeFile;
|
||||||
private NamingConfig _namingConfig;
|
private NamingConfig _namingConfig;
|
||||||
|
|
||||||
@ -53,6 +54,13 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
|||||||
.With(e => e.AbsoluteEpisodeNumber = 101)
|
.With(e => e.AbsoluteEpisodeNumber = 101)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
_episode3 = Builder<Episode>.CreateNew()
|
||||||
|
.With(e => e.Title = "City Sushi")
|
||||||
|
.With(e => e.SeasonNumber = 15)
|
||||||
|
.With(e => e.EpisodeNumber = 8)
|
||||||
|
.With(e => e.AbsoluteEpisodeNumber = 102)
|
||||||
|
.Build();
|
||||||
|
|
||||||
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "DRONE" };
|
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "DRONE" };
|
||||||
|
|
||||||
Mocker.GetMock<IQualityDefinitionService>()
|
Mocker.GetMock<IQualityDefinitionService>()
|
||||||
@ -549,8 +557,8 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
|||||||
_namingConfig.MultiEpisodeStyle = (int)MultiEpisodeStyle.Duplicate;
|
_namingConfig.MultiEpisodeStyle = (int)MultiEpisodeStyle.Duplicate;
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
||||||
.Should().Be("South Park - 100 - 101 - City Sushi");
|
.Should().Be("South Park - 100 - 101 - 102 - City Sushi");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -682,5 +690,59 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
|||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
||||||
.Should().Be("South Park - 15x06 - 15x07 - [100-101] - City Sushi - HDTV-720p");
|
.Should().Be("South Park - 15x06 - 15x07 - [100-101] - City Sushi - HDTV-720p");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_format_range_multi_episode_properly()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
||||||
|
_namingConfig.MultiEpisodeStyle = 4;
|
||||||
|
|
||||||
|
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
||||||
|
.Should().Be("South Park - S15E06-08 - City Sushi");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_format_range_multi_episode_anime_properly()
|
||||||
|
{
|
||||||
|
_series.SeriesType = SeriesTypes.Anime;
|
||||||
|
_namingConfig.MultiEpisodeStyle = 4;
|
||||||
|
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
||||||
|
|
||||||
|
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
||||||
|
.Should().Be("South Park - 100-102 - City Sushi");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_format_repeat_multi_episode_anime_properly()
|
||||||
|
{
|
||||||
|
_series.SeriesType = SeriesTypes.Anime;
|
||||||
|
_namingConfig.MultiEpisodeStyle = 2;
|
||||||
|
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
||||||
|
|
||||||
|
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
||||||
|
.Should().Be("South Park - 100-101-102 - City Sushi");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_format_single_episode_with_range_multi_episode_properly()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
||||||
|
_namingConfig.MultiEpisodeStyle = 4;
|
||||||
|
|
||||||
|
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
||||||
|
.Should().Be("South Park - S15E06 - City Sushi");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_format_single_anime_episode_with_range_multi_episode_properly()
|
||||||
|
{
|
||||||
|
_series.SeriesType = SeriesTypes.Anime;
|
||||||
|
_namingConfig.MultiEpisodeStyle = 4;
|
||||||
|
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
||||||
|
|
||||||
|
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
||||||
|
.Should().Be("South Park - 100 - City Sushi");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -97,6 +97,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
|
|
||||||
[TestCase("[ANBU-AonE]_Naruto_26-27_[F224EF26].avi", "Naruto", new[] { 26, 27 })]
|
[TestCase("[ANBU-AonE]_Naruto_26-27_[F224EF26].avi", "Naruto", new[] { 26, 27 })]
|
||||||
[TestCase("[Doutei] Recently, My Sister is Unusual - 01-12 [BD][720p-AAC]", "Recently, My Sister is Unusual", new [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 })]
|
[TestCase("[Doutei] Recently, My Sister is Unusual - 01-12 [BD][720p-AAC]", "Recently, My Sister is Unusual", new [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 })]
|
||||||
|
[TestCase("Series Title (2010) - 01-02-03 - Episode Title (1) HDTV-720p", "Series Title (2010)", new [] { 1, 2, 3 })]
|
||||||
public void should_parse_multi_episode_absolute_numbers(string postTitle, string title, int[] absoluteEpisodeNumbers)
|
public void should_parse_multi_episode_absolute_numbers(string postTitle, string title, int[] absoluteEpisodeNumbers)
|
||||||
{
|
{
|
||||||
var result = Parser.Parser.ParseTitle(postTitle);
|
var result = Parser.Parser.ParseTitle(postTitle);
|
||||||
|
@ -280,32 +280,41 @@ namespace NzbDrone.Core.Organizer
|
|||||||
foreach (var episodeFormat in episodeFormats)
|
foreach (var episodeFormat in episodeFormats)
|
||||||
{
|
{
|
||||||
var seasonEpisodePattern = episodeFormat.SeasonEpisodePattern;
|
var seasonEpisodePattern = episodeFormat.SeasonEpisodePattern;
|
||||||
|
string formatPattern;
|
||||||
|
|
||||||
foreach (var episode in episodes.Skip(1))
|
switch ((MultiEpisodeStyle)namingConfig.MultiEpisodeStyle)
|
||||||
{
|
{
|
||||||
switch ((MultiEpisodeStyle)namingConfig.MultiEpisodeStyle)
|
case MultiEpisodeStyle.Duplicate:
|
||||||
{
|
formatPattern = episodeFormat.Separator + episodeFormat.SeasonEpisodePattern;
|
||||||
case MultiEpisodeStyle.Duplicate:
|
seasonEpisodePattern = FormatNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
||||||
seasonEpisodePattern += episodeFormat.Separator + episodeFormat.SeasonEpisodePattern;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.Repeat:
|
case MultiEpisodeStyle.Repeat:
|
||||||
seasonEpisodePattern += episodeFormat.EpisodeSeparator + episodeFormat.EpisodePattern;
|
formatPattern = episodeFormat.EpisodeSeparator + episodeFormat.EpisodePattern;
|
||||||
break;
|
seasonEpisodePattern = FormatNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
||||||
|
break;
|
||||||
|
|
||||||
case MultiEpisodeStyle.Scene:
|
case MultiEpisodeStyle.Scene:
|
||||||
seasonEpisodePattern += "-" + episodeFormat.EpisodeSeparator + episodeFormat.EpisodePattern;
|
formatPattern = "-" + episodeFormat.EpisodeSeparator + episodeFormat.EpisodePattern;
|
||||||
break;
|
seasonEpisodePattern = FormatNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
||||||
|
break;
|
||||||
|
|
||||||
//MultiEpisodeStyle.Extend
|
case MultiEpisodeStyle.Range:
|
||||||
default:
|
formatPattern = "-" + episodeFormat.EpisodePattern;
|
||||||
seasonEpisodePattern += "-" + episodeFormat.EpisodePattern;
|
var eps = new List<Episode> { episodes.First() };
|
||||||
break;
|
|
||||||
}
|
if (episodes.Count > 1) eps.Add(episodes.Last());
|
||||||
|
|
||||||
|
seasonEpisodePattern = FormatNumberTokens(seasonEpisodePattern, formatPattern, eps);
|
||||||
|
break;
|
||||||
|
|
||||||
|
//MultiEpisodeStyle.Extend
|
||||||
|
default:
|
||||||
|
formatPattern = "-" + episodeFormat.EpisodePattern;
|
||||||
|
seasonEpisodePattern = FormatNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
seasonEpisodePattern = ReplaceNumberTokens(seasonEpisodePattern, episodes);
|
|
||||||
|
|
||||||
var token = String.Format("{{Season Episode{0}}}", index++);
|
var token = String.Format("{{Season Episode{0}}}", index++);
|
||||||
pattern = pattern.Replace(episodeFormat.SeasonEpisodePattern, token);
|
pattern = pattern.Replace(episodeFormat.SeasonEpisodePattern, token);
|
||||||
tokenHandlers[token] = m => seasonEpisodePattern;
|
tokenHandlers[token] = m => seasonEpisodePattern;
|
||||||
@ -339,34 +348,44 @@ namespace NzbDrone.Core.Organizer
|
|||||||
}
|
}
|
||||||
|
|
||||||
var absoluteEpisodePattern = absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
var absoluteEpisodePattern = absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
||||||
|
string formatPattern;
|
||||||
|
|
||||||
foreach (var episode in episodes.Skip(1))
|
switch ((MultiEpisodeStyle) namingConfig.MultiEpisodeStyle)
|
||||||
{
|
{
|
||||||
switch ((MultiEpisodeStyle)namingConfig.MultiEpisodeStyle)
|
|
||||||
{
|
|
||||||
case MultiEpisodeStyle.Duplicate:
|
|
||||||
absoluteEpisodePattern += absoluteEpisodeFormat.Separator +
|
|
||||||
absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.Repeat:
|
case MultiEpisodeStyle.Duplicate:
|
||||||
absoluteEpisodePattern += absoluteEpisodeFormat.Separator +
|
formatPattern = absoluteEpisodeFormat.Separator + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
||||||
absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, episodes);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MultiEpisodeStyle.Scene:
|
case MultiEpisodeStyle.Repeat:
|
||||||
absoluteEpisodePattern += "-" + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
var repeatSeparator = absoluteEpisodeFormat.Separator.Trim().IsNullOrWhiteSpace() ? " " : absoluteEpisodeFormat.Separator.Trim();
|
||||||
break;
|
|
||||||
|
formatPattern = repeatSeparator + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
||||||
|
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, episodes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MultiEpisodeStyle.Scene:
|
||||||
|
formatPattern = "-" + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
||||||
|
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, episodes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MultiEpisodeStyle.Range:
|
||||||
|
formatPattern = "-" + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
||||||
|
var eps = new List<Episode> {episodes.First()};
|
||||||
|
|
||||||
|
if (episodes.Count > 1) eps.Add(episodes.Last());
|
||||||
|
|
||||||
|
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, eps);
|
||||||
|
break;
|
||||||
|
|
||||||
//MultiEpisodeStyle.Extend
|
//MultiEpisodeStyle.Extend
|
||||||
default:
|
default:
|
||||||
absoluteEpisodePattern += "-" + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
formatPattern = "-" + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
||||||
break;
|
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, episodes);
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
absoluteEpisodePattern = ReplaceAbsoluteNumberTokens(absoluteEpisodePattern, episodes);
|
|
||||||
|
|
||||||
var token = String.Format("{{Absolute Pattern{0}}}", index++);
|
var token = String.Format("{{Absolute Pattern{0}}}", index++);
|
||||||
pattern = pattern.Replace(absoluteEpisodeFormat.AbsoluteEpisodePattern, token);
|
pattern = pattern.Replace(absoluteEpisodeFormat.AbsoluteEpisodePattern, token);
|
||||||
tokenHandlers[token] = m => absoluteEpisodePattern;
|
tokenHandlers[token] = m => absoluteEpisodePattern;
|
||||||
@ -554,31 +573,30 @@ namespace NzbDrone.Core.Organizer
|
|||||||
return replacementText;
|
return replacementText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReplaceNumberTokens(string pattern, List<Episode> episodes)
|
private string FormatNumberTokens(string basePattern, string formatPattern, List<Episode> episodes)
|
||||||
{
|
{
|
||||||
var episodeIndex = 0;
|
var pattern = String.Empty;
|
||||||
pattern = EpisodeRegex.Replace(pattern, match =>
|
|
||||||
{
|
|
||||||
var episode = episodes[episodeIndex];
|
|
||||||
episodeIndex++;
|
|
||||||
|
|
||||||
return ReplaceNumberToken(match.Groups["episode"].Value, episode.EpisodeNumber);
|
for (int i = 0; i < episodes.Count; i++)
|
||||||
});
|
{
|
||||||
|
var patternToReplace = i == 0 ? basePattern : formatPattern;
|
||||||
|
|
||||||
|
pattern += EpisodeRegex.Replace(patternToReplace, match => ReplaceNumberToken(match.Groups["episode"].Value, episodes[i].EpisodeNumber));
|
||||||
|
}
|
||||||
|
|
||||||
return ReplaceSeasonTokens(pattern, episodes.First().SeasonNumber);
|
return ReplaceSeasonTokens(pattern, episodes.First().SeasonNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReplaceAbsoluteNumberTokens(string pattern, List<Episode> episodes)
|
private string FormatAbsoluteNumberTokens(string basePattern, string formatPattern, List<Episode> episodes)
|
||||||
{
|
{
|
||||||
var episodeIndex = 0;
|
var pattern = String.Empty;
|
||||||
pattern = AbsoluteEpisodeRegex.Replace(pattern, match =>
|
|
||||||
{
|
|
||||||
var episode = episodes[episodeIndex];
|
|
||||||
episodeIndex++;
|
|
||||||
|
|
||||||
//TODO: We need to handle this null check somewhere, I think earlier is better...
|
for (int i = 0; i < episodes.Count; i++)
|
||||||
return ReplaceNumberToken(match.Groups["absolute"].Value, episode.AbsoluteEpisodeNumber.Value);
|
{
|
||||||
});
|
var patternToReplace = i == 0 ? basePattern : formatPattern;
|
||||||
|
|
||||||
|
pattern += AbsoluteEpisodeRegex.Replace(patternToReplace, match => ReplaceNumberToken(match.Groups["absolute"].Value, episodes[i].AbsoluteEpisodeNumber.Value));
|
||||||
|
}
|
||||||
|
|
||||||
return ReplaceSeasonTokens(pattern, episodes.First().SeasonNumber);
|
return ReplaceSeasonTokens(pattern, episodes.First().SeasonNumber);
|
||||||
}
|
}
|
||||||
@ -684,6 +702,7 @@ namespace NzbDrone.Core.Organizer
|
|||||||
Extend = 0,
|
Extend = 0,
|
||||||
Duplicate = 1,
|
Duplicate = 1,
|
||||||
Repeat = 2,
|
Repeat = 2,
|
||||||
Scene = 3
|
Scene = 3,
|
||||||
|
Range = 4
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,6 +13,7 @@ namespace NzbDrone.Core.Organizer
|
|||||||
SampleResult GetMultiEpisodeSample(NamingConfig nameSpec);
|
SampleResult GetMultiEpisodeSample(NamingConfig nameSpec);
|
||||||
SampleResult GetDailySample(NamingConfig nameSpec);
|
SampleResult GetDailySample(NamingConfig nameSpec);
|
||||||
SampleResult GetAnimeSample(NamingConfig nameSpec);
|
SampleResult GetAnimeSample(NamingConfig nameSpec);
|
||||||
|
SampleResult GetAnimeMultiEpisodeSample(NamingConfig nameSpec);
|
||||||
String GetSeriesFolderSample(NamingConfig nameSpec);
|
String GetSeriesFolderSample(NamingConfig nameSpec);
|
||||||
String GetSeasonFolderSample(NamingConfig nameSpec);
|
String GetSeasonFolderSample(NamingConfig nameSpec);
|
||||||
}
|
}
|
||||||
@ -25,12 +26,14 @@ namespace NzbDrone.Core.Organizer
|
|||||||
private static Series _animeSeries;
|
private static Series _animeSeries;
|
||||||
private static Episode _episode1;
|
private static Episode _episode1;
|
||||||
private static Episode _episode2;
|
private static Episode _episode2;
|
||||||
|
private static Episode _episode3;
|
||||||
private static List<Episode> _singleEpisode;
|
private static List<Episode> _singleEpisode;
|
||||||
private static List<Episode> _multiEpisodes;
|
private static List<Episode> _multiEpisodes;
|
||||||
private static EpisodeFile _singleEpisodeFile;
|
private static EpisodeFile _singleEpisodeFile;
|
||||||
private static EpisodeFile _multiEpisodeFile;
|
private static EpisodeFile _multiEpisodeFile;
|
||||||
private static EpisodeFile _dailyEpisodeFile;
|
private static EpisodeFile _dailyEpisodeFile;
|
||||||
private static EpisodeFile _animeEpisodeFile;
|
private static EpisodeFile _animeEpisodeFile;
|
||||||
|
private static EpisodeFile _animeMultiEpisodeFile;
|
||||||
|
|
||||||
public FileNameSampleService(IBuildFileNames buildFileNames)
|
public FileNameSampleService(IBuildFileNames buildFileNames)
|
||||||
{
|
{
|
||||||
@ -71,8 +74,16 @@ namespace NzbDrone.Core.Organizer
|
|||||||
AbsoluteEpisodeNumber = 2
|
AbsoluteEpisodeNumber = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_episode3 = new Episode
|
||||||
|
{
|
||||||
|
SeasonNumber = 1,
|
||||||
|
EpisodeNumber = 3,
|
||||||
|
Title = "Episode Title (3)",
|
||||||
|
AbsoluteEpisodeNumber = 3
|
||||||
|
};
|
||||||
|
|
||||||
_singleEpisode = new List<Episode> { _episode1 };
|
_singleEpisode = new List<Episode> { _episode1 };
|
||||||
_multiEpisodes = new List<Episode> { _episode1, _episode2 };
|
_multiEpisodes = new List<Episode> { _episode1, _episode2, _episode3 };
|
||||||
|
|
||||||
var mediaInfo = new MediaInfoModel()
|
var mediaInfo = new MediaInfoModel()
|
||||||
{
|
{
|
||||||
@ -102,8 +113,8 @@ namespace NzbDrone.Core.Organizer
|
|||||||
_multiEpisodeFile = new EpisodeFile
|
_multiEpisodeFile = new EpisodeFile
|
||||||
{
|
{
|
||||||
Quality = new QualityModel(Quality.HDTV720p),
|
Quality = new QualityModel(Quality.HDTV720p),
|
||||||
RelativePath = "Series.Title.S01E01-E02.720p.HDTV.x264-EVOLVE.mkv",
|
RelativePath = "Series.Title.S01E01-E03.720p.HDTV.x264-EVOLVE.mkv",
|
||||||
SceneName = "Series.Title.S01E01-E02.720p.HDTV.x264-EVOLVE",
|
SceneName = "Series.Title.S01E01-E03.720p.HDTV.x264-EVOLVE",
|
||||||
ReleaseGroup = "RlsGrp",
|
ReleaseGroup = "RlsGrp",
|
||||||
MediaInfo = mediaInfo,
|
MediaInfo = mediaInfo,
|
||||||
};
|
};
|
||||||
@ -120,8 +131,17 @@ namespace NzbDrone.Core.Organizer
|
|||||||
_animeEpisodeFile = new EpisodeFile
|
_animeEpisodeFile = new EpisodeFile
|
||||||
{
|
{
|
||||||
Quality = new QualityModel(Quality.HDTV720p),
|
Quality = new QualityModel(Quality.HDTV720p),
|
||||||
RelativePath = "Series.Title.001.HDTV.x264-EVOLVE.mkv",
|
RelativePath = "[RlsGroup] Series Title - 001 [720p].mkv",
|
||||||
SceneName = "Series.Title.001.HDTV.x264-EVOLVE",
|
SceneName = "[RlsGroup] Series Title - 001 [720p]",
|
||||||
|
ReleaseGroup = "RlsGrp",
|
||||||
|
MediaInfo = mediaInfoAnime
|
||||||
|
};
|
||||||
|
|
||||||
|
_animeMultiEpisodeFile = new EpisodeFile
|
||||||
|
{
|
||||||
|
Quality = new QualityModel(Quality.HDTV720p),
|
||||||
|
RelativePath = "[RlsGroup] Series Title - 001 - 103 [720p].mkv",
|
||||||
|
SceneName = "[RlsGroup] Series Title - 001 - 103 [720p]",
|
||||||
ReleaseGroup = "RlsGrp",
|
ReleaseGroup = "RlsGrp",
|
||||||
MediaInfo = mediaInfoAnime
|
MediaInfo = mediaInfoAnime
|
||||||
};
|
};
|
||||||
@ -179,6 +199,19 @@ namespace NzbDrone.Core.Organizer
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SampleResult GetAnimeMultiEpisodeSample(NamingConfig nameSpec)
|
||||||
|
{
|
||||||
|
var result = new SampleResult
|
||||||
|
{
|
||||||
|
FileName = BuildSample(_multiEpisodes, _animeSeries, _animeMultiEpisodeFile, nameSpec),
|
||||||
|
Series = _animeSeries,
|
||||||
|
Episodes = _multiEpisodes,
|
||||||
|
EpisodeFile = _animeMultiEpisodeFile
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public string GetSeriesFolderSample(NamingConfig nameSpec)
|
public string GetSeriesFolderSample(NamingConfig nameSpec)
|
||||||
{
|
{
|
||||||
return _buildFileNames.GetSeriesFolder(_standardSeries, nameSpec);
|
return _buildFileNames.GetSeriesFolder(_standardSeries, nameSpec);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
@ -87,7 +87,7 @@ namespace NzbDrone.Core.Parser
|
|||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
|
|
||||||
//Episodes with airdate
|
//Episodes with airdate
|
||||||
new Regex(@"^(?<title>.+?)?\W*(?<airyear>\d{4})\W+(?<airmonth>[0-1][0-9])\W+(?<airday>[0-3][0-9])",
|
new Regex(@"^(?<title>.+?)?\W*(?<airyear>\d{4})\W+(?<airmonth>[0-1][0-9])\W+(?<airday>[0-3][0-9])(?!\W+[0-3][0-9])",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
|
|
||||||
//Supports 1103/1113 naming
|
//Supports 1103/1113 naming
|
||||||
|
@ -13,16 +13,17 @@ define(
|
|||||||
template: 'Settings/MediaManagement/Naming/NamingViewTemplate',
|
template: 'Settings/MediaManagement/Naming/NamingViewTemplate',
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
namingOptions : '.x-naming-options',
|
namingOptions : '.x-naming-options',
|
||||||
renameEpisodesCheckbox: '.x-rename-episodes',
|
renameEpisodesCheckbox : '.x-rename-episodes',
|
||||||
singleEpisodeExample : '.x-single-episode-example',
|
singleEpisodeExample : '.x-single-episode-example',
|
||||||
multiEpisodeExample : '.x-multi-episode-example',
|
multiEpisodeExample : '.x-multi-episode-example',
|
||||||
dailyEpisodeExample : '.x-daily-episode-example',
|
dailyEpisodeExample : '.x-daily-episode-example',
|
||||||
animeEpisodeExample : '.x-anime-episode-example',
|
animeEpisodeExample : '.x-anime-episode-example',
|
||||||
namingTokenHelper : '.x-naming-token-helper',
|
animeMultiEpisodeExample : '.x-anime-multi-episode-example',
|
||||||
multiEpisodeStyle : '.x-multi-episode-style',
|
namingTokenHelper : '.x-naming-token-helper',
|
||||||
seriesFolderExample : '.x-series-folder-example',
|
multiEpisodeStyle : '.x-multi-episode-style',
|
||||||
seasonFolderExample : '.x-season-folder-example'
|
seriesFolderExample : '.x-series-folder-example',
|
||||||
|
seasonFolderExample : '.x-season-folder-example'
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
@ -70,6 +71,7 @@ define(
|
|||||||
this.ui.multiEpisodeExample.html(this.namingSampleModel.get('multiEpisodeExample'));
|
this.ui.multiEpisodeExample.html(this.namingSampleModel.get('multiEpisodeExample'));
|
||||||
this.ui.dailyEpisodeExample.html(this.namingSampleModel.get('dailyEpisodeExample'));
|
this.ui.dailyEpisodeExample.html(this.namingSampleModel.get('dailyEpisodeExample'));
|
||||||
this.ui.animeEpisodeExample.html(this.namingSampleModel.get('animeEpisodeExample'));
|
this.ui.animeEpisodeExample.html(this.namingSampleModel.get('animeEpisodeExample'));
|
||||||
|
this.ui.animeMultiEpisodeExample.html(this.namingSampleModel.get('animeMultiEpisodeExample'));
|
||||||
this.ui.seriesFolderExample.html(this.namingSampleModel.get('seriesFolderExample'));
|
this.ui.seriesFolderExample.html(this.namingSampleModel.get('seriesFolderExample'));
|
||||||
this.ui.seasonFolderExample.html(this.namingSampleModel.get('seasonFolderExample'));
|
this.ui.seasonFolderExample.html(this.namingSampleModel.get('seasonFolderExample'));
|
||||||
},
|
},
|
||||||
|
@ -175,6 +175,7 @@
|
|||||||
<option value="1">Duplicate</option>
|
<option value="1">Duplicate</option>
|
||||||
<option value="2">Repeat</option>
|
<option value="2">Repeat</option>
|
||||||
<option value="3">Scene</option>
|
<option value="3">Scene</option>
|
||||||
|
<option value="4">Range</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -212,6 +213,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-3 control-label">Anime Multi-Episode Example</label>
|
||||||
|
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<p class="form-control-static x-anime-multi-episode-example naming-example"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">Series Folder Example</label>
|
<label class="col-sm-3 control-label">Series Folder Example</label>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user