diff --git a/src/NzbDrone.Api/Config/NamingConfigResource.cs b/src/NzbDrone.Api/Config/NamingConfigResource.cs index 9ca779dac..1e5ab16e3 100644 --- a/src/NzbDrone.Api/Config/NamingConfigResource.cs +++ b/src/NzbDrone.Api/Config/NamingConfigResource.cs @@ -9,8 +9,8 @@ namespace NzbDrone.Api.Config public Int32 MultiEpisodeStyle { get; set; } public string StandardEpisodeFormat { get; set; } public string DailyEpisodeFormat { get; set; } + public string SeriesFolderFormat { get; set; } public string SeasonFolderFormat { get; set; } - public bool IncludeSeriesTitle { get; set; } public bool IncludeEpisodeTitle { get; set; } public bool IncludeQuality { get; set; } diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index f4a352f2e..dd0723b87 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -176,6 +176,7 @@ + diff --git a/src/NzbDrone.Core.Test/OrganizerTests/GetSeriesFolderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/GetSeriesFolderFixture.cs new file mode 100644 index 000000000..abe7bf78b --- /dev/null +++ b/src/NzbDrone.Core.Test/OrganizerTests/GetSeriesFolderFixture.cs @@ -0,0 +1,33 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.OrganizerTests +{ + [TestFixture] + + public class GetSeriesFolderFixture : CoreTest + { + private NamingConfig namingConfig; + + [SetUp] + public void Setup() + { + namingConfig = new NamingConfig(); + + Mocker.GetMock() + .Setup(c => c.GetConfig()).Returns(namingConfig); + } + + [TestCase("30 Rock", "{Series Title}", "30 Rock")] + [TestCase("30 Rock", "{Series.Title}", "30.Rock")] + [TestCase("24/7 Road to the NHL Winter Classic", "{Series Title}", "24+7 Road to the NHL Winter Classic")] + public void should_use_seriesFolderFormat_to_build_folder_name(string seriesTitle, string format, string expected) + { + namingConfig.SeriesFolderFormat = format; + + Subject.GetSeriesFolder(seriesTitle).Should().Be(expected); + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/Migration/035_add_series_folder_format_to_naming_config.cs b/src/NzbDrone.Core/Datastore/Migration/035_add_series_folder_format_to_naming_config.cs new file mode 100644 index 000000000..9423a54f0 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/035_add_series_folder_format_to_naming_config.cs @@ -0,0 +1,16 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(35)] + public class add_series_folder_format_to_naming_config : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("NamingConfig").AddColumn("SeriesFolderFormat").AsString().Nullable(); + + Execute.Sql("UPDATE NamingConfig SET SeriesFolderFormat = '{Series Title}'"); + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 4567bc73a..5dbf4fd48 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -190,6 +190,7 @@ + diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 4a1abfed5..905b73456 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -16,6 +16,7 @@ namespace NzbDrone.Core.Organizer string BuildFilename(IList episodes, Series series, EpisodeFile episodeFile, NamingConfig namingConfig); string BuildFilePath(Series series, int seasonNumber, string fileName, string extension); BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec); + string GetSeriesFolder(string seriesTitle); } public class FileNameBuilder : IBuildFileNames @@ -151,6 +152,7 @@ namespace NzbDrone.Core.Organizer public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension) { string path = series.Path; + if (series.SeasonFolder) { string seasonFolder; @@ -222,6 +224,17 @@ namespace NzbDrone.Core.Organizer return basicNamingConfig; } + public string GetSeriesFolder(string seriesTitle) + { + seriesTitle = CleanFilename(seriesTitle); + + var nameSpec = _namingConfigService.GetConfig(); + var tokenValues = new Dictionary(FilenameBuilderTokenEqualityComparer.Instance); + tokenValues.Add("{Series Title}", seriesTitle); + + return ReplaceTokens(nameSpec.SeriesFolderFormat, tokenValues); + } + public static string CleanFilename(string name) { string result = name; diff --git a/src/NzbDrone.Core/Organizer/NamingConfig.cs b/src/NzbDrone.Core/Organizer/NamingConfig.cs index 7a537e3ce..fcfcdd940 100644 --- a/src/NzbDrone.Core/Organizer/NamingConfig.cs +++ b/src/NzbDrone.Core/Organizer/NamingConfig.cs @@ -14,6 +14,7 @@ namespace NzbDrone.Core.Organizer MultiEpisodeStyle = 0, StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Title}", DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title} {Quality Title}", + SeriesFolderFormat = "{Series Title}", SeasonFolderFormat = "Season {season}" }; } @@ -23,6 +24,7 @@ namespace NzbDrone.Core.Organizer public int MultiEpisodeStyle { get; set; } public string StandardEpisodeFormat { get; set; } public string DailyEpisodeFormat { get; set; } + public string SeriesFolderFormat { get; set; } public string SeasonFolderFormat { get; set; } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Tv/SeriesService.cs b/src/NzbDrone.Core/Tv/SeriesService.cs index 18b67a732..5387f3704 100644 --- a/src/NzbDrone.Core/Tv/SeriesService.cs +++ b/src/NzbDrone.Core/Tv/SeriesService.cs @@ -31,24 +31,24 @@ namespace NzbDrone.Core.Tv public class SeriesService : ISeriesService { private readonly ISeriesRepository _seriesRepository; - private readonly IConfigService _configService; private readonly IEventAggregator _eventAggregator; private readonly ISceneMappingService _sceneMappingService; private readonly IEpisodeService _episodeService; + private readonly IBuildFileNames _fileNameBuilder; private readonly Logger _logger; public SeriesService(ISeriesRepository seriesRepository, - IConfigService configServiceService, IEventAggregator eventAggregator, ISceneMappingService sceneMappingService, IEpisodeService episodeService, + IBuildFileNames fileNameBuilder, Logger logger) { _seriesRepository = seriesRepository; - _configService = configServiceService; _eventAggregator = eventAggregator; _sceneMappingService = sceneMappingService; _episodeService = episodeService; + _fileNameBuilder = fileNameBuilder; _logger = logger; } @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Tv if (String.IsNullOrWhiteSpace(newSeries.Path)) { - var folderName = FileNameBuilder.CleanFilename(newSeries.Title); + var folderName = _fileNameBuilder.GetSeriesFolder(newSeries.Title); newSeries.Path = Path.Combine(newSeries.RootFolderPath, folderName); } diff --git a/src/UI/Settings/MediaManagement/Naming/NamingViewTemplate.html b/src/UI/Settings/MediaManagement/Naming/NamingViewTemplate.html index 33586db4a..af3839918 100644 --- a/src/UI/Settings/MediaManagement/Naming/NamingViewTemplate.html +++ b/src/UI/Settings/MediaManagement/Naming/NamingViewTemplate.html @@ -98,6 +98,24 @@ +
+ + +
+
+ +
+ + +
+
+
+
+