diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj
index fa0396a3d..53cb10061 100644
--- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj
+++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj
@@ -336,6 +336,9 @@
+
+
+
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleYearFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleYearFixture.cs
new file mode 100644
index 000000000..7a4bc5a2d
--- /dev/null
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleYearFixture.cs
@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using System.Linq;
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.MediaFiles;
+using NzbDrone.Core.Organizer;
+using NzbDrone.Core.Qualities;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Core.Tv;
+
+namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
+{
+ [TestFixture]
+ public class CleanTitleYearFixture : CoreTest
+ {
+ private Series _series;
+ private Episode _episode;
+ private EpisodeFile _episodeFile;
+ private NamingConfig _namingConfig;
+
+ [SetUp]
+ public void Setup()
+ {
+ _series = Builder
+ .CreateNew()
+ .Build();
+
+ _episode = Builder.CreateNew()
+ .With(e => e.Title = "City Sushi")
+ .With(e => e.SeasonNumber = 15)
+ .With(e => e.EpisodeNumber = 6)
+ .With(e => e.AbsoluteEpisodeNumber = 100)
+ .Build();
+
+ _episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "SonarrTest" };
+
+ _namingConfig = NamingConfig.Default;
+ _namingConfig.RenameEpisodes = true;
+
+ Mocker.GetMock()
+ .Setup(c => c.GetConfig()).Returns(_namingConfig);
+
+ Mocker.GetMock()
+ .Setup(v => v.Get(Moq.It.IsAny()))
+ .Returns(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
+ }
+
+ [TestCase("The Mist", 2018, "The Mist 2018")]
+ [TestCase("The Rat Pack (A&E)", 1999, "The Rat Pack AandE 1999")]
+ [TestCase("The Climax: I (Almost) Got Away With It (2016)", 2016, "The Climax I Almost Got Away With It 2016")]
+ public void should_get_expected_title_back(string title, int year, string expected)
+ {
+ _series.Title = title;
+ _series.Year = year;
+ _namingConfig.StandardEpisodeFormat = "{Series CleanTitleYear}";
+
+ Subject.BuildFileName(new List { _episode }, _series, _episodeFile)
+ .Should().Be(expected);
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs
index e28bc58f5..fdc1069fe 100644
--- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs
@@ -491,8 +491,10 @@ public void should_format_mediainfo_properly()
{
VideoCodec = "AVC",
AudioFormat = "DTS",
+ AudioChannels = 6,
AudioLanguages = "English/Spanish",
- Subtitles = "English/Spanish/Italian"
+ Subtitles = "English/Spanish/Italian",
+ SchemaRevision = 3
};
Subject.BuildFileName(new List { _episode1 }, _series, _episodeFile)
@@ -508,8 +510,10 @@ public void should_exclude_english_in_mediainfo_audio_language()
{
VideoCodec = "AVC",
AudioFormat = "DTS",
+ AudioChannels = 6,
AudioLanguages = "English",
- Subtitles = "English/Spanish/Italian"
+ Subtitles = "English/Spanish/Italian",
+ SchemaRevision = 3
};
Subject.BuildFileName(new List { _episode1 }, _series, _episodeFile)
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheFixture.cs
index 8145ead25..dcdded4eb 100644
--- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheFixture.cs
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheFixture.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
@@ -24,7 +24,6 @@ public void Setup()
{
_series = Builder
.CreateNew()
- .With(s => s.Title = "South Park")
.Build();
_episode = Builder.CreateNew()
@@ -57,7 +56,6 @@ public void Setup()
[TestCase("The Amazing Race (Latin America)", "Amazing Race, The (Latin America)")]
[TestCase("The Rat Pack (A&E)", "Rat Pack, The (A&E)")]
[TestCase("The Climax: I (Almost) Got Away With It (2016)", "Climax- I (Almost) Got Away With It, The (2016)")]
- //[TestCase("", "")]
public void should_get_expected_title_back(string title, string expected)
{
_series.Title = title;
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheYearFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheYearFixture.cs
new file mode 100644
index 000000000..f55ebacdc
--- /dev/null
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleTheYearFixture.cs
@@ -0,0 +1,63 @@
+using System.Collections.Generic;
+using System.Linq;
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.MediaFiles;
+using NzbDrone.Core.Organizer;
+using NzbDrone.Core.Qualities;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Core.Tv;
+
+namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
+{
+ [TestFixture]
+ public class TitleTheYearFixture : CoreTest
+ {
+ private Series _series;
+ private Episode _episode;
+ private EpisodeFile _episodeFile;
+ private NamingConfig _namingConfig;
+
+ [SetUp]
+ public void Setup()
+ {
+ _series = Builder
+ .CreateNew()
+ .Build();
+
+ _episode = Builder.CreateNew()
+ .With(e => e.Title = "City Sushi")
+ .With(e => e.SeasonNumber = 15)
+ .With(e => e.EpisodeNumber = 6)
+ .With(e => e.AbsoluteEpisodeNumber = 100)
+ .Build();
+
+ _episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "SonarrTest" };
+
+ _namingConfig = NamingConfig.Default;
+ _namingConfig.RenameEpisodes = true;
+
+ Mocker.GetMock()
+ .Setup(c => c.GetConfig()).Returns(_namingConfig);
+
+ Mocker.GetMock()
+ .Setup(v => v.Get(Moq.It.IsAny()))
+ .Returns(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
+ }
+
+ [TestCase("The Mist", 2018, "Mist, The (2018)")]
+ [TestCase("The Rat Pack (A&E)", 1999, "Rat Pack, The (A&E) (1999)")]
+ [TestCase("The Climax: I (Almost) Got Away With It (2016)", 2016, "Climax- I (Almost) Got Away With It, The (2016)")]
+ [TestCase("A", 2017, "A (2017)")]
+ public void should_get_expected_title_back(string title, int year, string expected)
+ {
+ _series.Title = title;
+ _series.Year = year;
+ _namingConfig.StandardEpisodeFormat = "{Series TitleTheYear}";
+
+ Subject.BuildFileName(new List { _episode }, _series, _episodeFile)
+ .Should().Be(expected);
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleYearFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleYearFixture.cs
new file mode 100644
index 000000000..1c2f03a94
--- /dev/null
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TitleYearFixture.cs
@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using System.Linq;
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.MediaFiles;
+using NzbDrone.Core.Organizer;
+using NzbDrone.Core.Qualities;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Core.Tv;
+
+namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
+{
+ [TestFixture]
+ public class TitleYearFixture : CoreTest
+ {
+ private Series _series;
+ private Episode _episode;
+ private EpisodeFile _episodeFile;
+ private NamingConfig _namingConfig;
+
+ [SetUp]
+ public void Setup()
+ {
+ _series = Builder
+ .CreateNew()
+ .Build();
+
+ _episode = Builder.CreateNew()
+ .With(e => e.Title = "City Sushi")
+ .With(e => e.SeasonNumber = 15)
+ .With(e => e.EpisodeNumber = 6)
+ .With(e => e.AbsoluteEpisodeNumber = 100)
+ .Build();
+
+ _episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "SonarrTest" };
+
+ _namingConfig = NamingConfig.Default;
+ _namingConfig.RenameEpisodes = true;
+
+ Mocker.GetMock()
+ .Setup(c => c.GetConfig()).Returns(_namingConfig);
+
+ Mocker.GetMock()
+ .Setup(v => v.Get(Moq.It.IsAny()))
+ .Returns(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
+ }
+
+ [TestCase("The Mist", 2018, "The Mist (2018)")]
+ [TestCase("The Rat Pack (A&E)", 1999, "The Rat Pack (A&E) (1999)")]
+ [TestCase("The Climax: I (Almost) Got Away With It (2016)", 2016, "The Climax- I (Almost) Got Away With It (2016)")]
+ public void should_get_expected_title_back(string title, int year, string expected)
+ {
+ _series.Title = title;
+ _series.Year = year;
+ _namingConfig.StandardEpisodeFormat = "{Series TitleYear}";
+
+ Subject.BuildFileName(new List { _episode }, _series, _episodeFile)
+ .Should().Be(expected);
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs
index 34f1e19c1..26d63a06b 100644
--- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs
+++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs
@@ -55,7 +55,7 @@ public class FileNameBuilder : IBuildFileNames
public static readonly Regex AirDateRegex = new Regex(@"\{Air(\s|\W|_)Date\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
- public static readonly Regex SeriesTitleRegex = new Regex(@"(?\{(?:Series)(?[- ._])(Clean)?Title(The)?\})",
+ public static readonly Regex SeriesTitleRegex = new Regex(@"(?\{(?:Series)(?[- ._])(Clean)?Title(The)?(Year)?\})",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled);
@@ -71,6 +71,8 @@ public class FileNameBuilder : IBuildFileNames
private static readonly Regex TitlePrefixRegex = new Regex(@"^(The|An|A) (.*?)((?: *\([^)]+\))*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private static readonly Regex YearRegex = new Regex(@"\(\d{4}\)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
public FileNameBuilder(INamingConfigService namingConfigService,
IQualityDefinitionService qualityDefinitionService,
ICacheManager cacheManager,
@@ -263,6 +265,17 @@ public static string TitleThe(string title)
return TitlePrefixRegex.Replace(title, "$2, $1$3");
}
+ public static string TitleYear(string title, int year)
+ {
+ // Regex match incase the year in the title doesn't match the year, for whatever reason.
+ if (YearRegex.IsMatch(title))
+ {
+ return title;
+ }
+
+ return $"{title} ({year})";
+ }
+
public static string CleanFileName(string name, bool replace = true)
{
string result = name;
@@ -321,7 +334,10 @@ private void AddSeriesTokens(Dictionary> tokenH
{
tokenHandlers["{Series Title}"] = m => series.Title;
tokenHandlers["{Series CleanTitle}"] = m => CleanTitle(series.Title);
+ tokenHandlers["{Series CleanTitleYear}"] = m => CleanTitle(TitleYear(series.Title, series.Year));
tokenHandlers["{Series TitleThe}"] = m => TitleThe(series.Title);
+ tokenHandlers["{Series TitleYear}"] = m => TitleYear(series.Title, series.Year);
+ tokenHandlers["{Series TitleTheYear}"] = m => TitleYear(TitleThe(series.Title), series.Year);
}
private string AddSeasonEpisodeNumberingTokens(string pattern, Dictionary> tokenHandlers, List episodes, NamingConfig namingConfig)
diff --git a/src/NzbDrone.Core/Organizer/FileNameSampleService.cs b/src/NzbDrone.Core/Organizer/FileNameSampleService.cs
index de1046485..78f076b5f 100644
--- a/src/NzbDrone.Core/Organizer/FileNameSampleService.cs
+++ b/src/NzbDrone.Core/Organizer/FileNameSampleService.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
@@ -41,19 +41,22 @@ public FileNameSampleService(IBuildFileNames buildFileNames)
_standardSeries = new Series
{
SeriesType = SeriesTypes.Standard,
- Title = "The Series Title (2010)"
+ Title = "The Series Title!",
+ Year = 2010
};
_dailySeries = new Series
{
SeriesType = SeriesTypes.Daily,
- Title = "The Series Title (2010)"
+ Title = "The Series Title!",
+ Year = 2010
};
_animeSeries = new Series
{
SeriesType = SeriesTypes.Anime,
- Title = "The Series Title (2010)"
+ Title = "The Series Title!",
+ Year = 2010
};
_episode1 = new Episode