From 9eb022fdf40235eec23cc7bde7f3d52968497c8a Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 23 Jan 2012 22:29:32 -0800 Subject: [PATCH] BacklogStatus added to individually control which series are included in backlog searches. Applies to Backlog and RecentBacklog jobs. Editable in Series/MassEdit and Series Edit. --- .../JobTests/BacklogSearchJobTest.cs | 167 +++++++++++++++++- .../JobTests/RecentBacklogSearchJobTest.cs | 125 +++++++++++++ .../Datastore/Migrations/Migration20120123.cs | 15 ++ NzbDrone.Core/Jobs/BacklogSearchJob.cs | 31 +++- NzbDrone.Core/Jobs/RecentBacklogSearchJob.cs | 36 +++- NzbDrone.Core/Model/BacklogStatusType.cs | 9 + NzbDrone.Core/NzbDrone.Core.csproj | 2 + NzbDrone.Core/Providers/SeriesProvider.cs | 7 +- NzbDrone.Core/Repository/Series.cs | 3 + NzbDrone.Web/Controllers/SeriesController.cs | 28 ++- NzbDrone.Web/Models/SeriesModel.cs | 5 + .../Series/EditorTemplates/SeriesModel.cshtml | 4 + NzbDrone.Web/Views/Series/MassEdit.cshtml | 14 ++ NzbDrone.Web/Views/Series/SeriesItem.cshtml | 2 + 14 files changed, 423 insertions(+), 25 deletions(-) create mode 100644 NzbDrone.Core/Datastore/Migrations/Migration20120123.cs create mode 100644 NzbDrone.Core/Model/BacklogStatusType.cs diff --git a/NzbDrone.Core.Test/JobTests/BacklogSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/BacklogSearchJobTest.cs index dc3e758e8..173f05893 100644 --- a/NzbDrone.Core.Test/JobTests/BacklogSearchJobTest.cs +++ b/NzbDrone.Core.Test/JobTests/BacklogSearchJobTest.cs @@ -2,9 +2,11 @@ using System.Linq; using FizzWare.NBuilder; +using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Core.Jobs; +using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; @@ -53,7 +55,15 @@ namespace NzbDrone.Core.Test.JobTests //Setup var notification = new ProgressNotification("Backlog Search Job Test"); - var episodes = Builder.CreateListOfSize(1).Build(); + var series = Builder.CreateNew() + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .Build(); + + var episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.Series = series) + .Build(); WithStrictMocker(); WithEnableBacklogSearching(); @@ -81,7 +91,15 @@ namespace NzbDrone.Core.Test.JobTests //Setup var notification = new ProgressNotification("Backlog Search Job Test"); - var episodes = Builder.CreateListOfSize(5).Build(); + var series = Builder.CreateNew() + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .Build(); + + var episodes = Builder.CreateListOfSize(5) + .All() + .With(e => e.Series = series) + .Build(); WithStrictMocker(); WithEnableBacklogSearching(); @@ -109,9 +127,14 @@ namespace NzbDrone.Core.Test.JobTests //Setup var notification = new ProgressNotification("Backlog Search Job Test"); + var series = Builder.CreateNew() + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .Build(); + var episodes = Builder.CreateListOfSize(5) .All() - .With(e => e.SeriesId = 1) + .With(e => e.Series = series) .With(e => e.SeasonNumber = 1) .Build(); @@ -144,9 +167,15 @@ namespace NzbDrone.Core.Test.JobTests //Setup var notification = new ProgressNotification("Backlog Search Job Test"); + var series = Builder.CreateNew() + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .Build(); + var episodes = Builder.CreateListOfSize(5) .All() - .With(e => e.SeriesId = 1) + .With(e => e.Series = series) + .With(e => e.SeriesId = series.SeriesId) .With(e => e.SeasonNumber = 1) .Build(); @@ -179,10 +208,23 @@ namespace NzbDrone.Core.Test.JobTests //Setup var notification = new ProgressNotification("Backlog Search Job Test"); + var series = Builder.CreateNew() + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .Build(); + + var series2 = Builder.CreateNew() + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .Build(); + var episodes = Builder.CreateListOfSize(10) .TheFirst(5) - .With(e => e.SeriesId = 1) + .With(e => e.Series = series) + .With(e => e.SeriesId = series.SeriesId) .With(e => e.SeasonNumber = 1) + .TheNext(5) + .With(e => e.Series = series2) .Build(); WithStrictMocker(); @@ -210,5 +252,120 @@ namespace NzbDrone.Core.Test.JobTests Mocker.GetMock().Verify(c => c.Start(notification, It.IsAny(), 0), Times.Exactly(5)); } + + [Test] + public void GetMissingForEnabledSeries_should_only_return_episodes_for_monitored_series() + { + //Setup + var series = Builder.CreateListOfSize(2) + .TheFirst(1) + .With(s => s.Monitored = false) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .Build(); + + var episodes = Builder.CreateListOfSize(11) + .TheFirst(5) + .With(e => e.Series = series[0]) + .With(e => e.SeasonNumber = 1) + .TheLast(6) + .With(e => e.Series = series[1]) + .Build(); + + WithEnableBacklogSearching(); + + Mocker.GetMock() + .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); + + //Act + var result = Mocker.Resolve().GetMissingForEnabledSeries(); + + //Assert + result.Should().NotBeEmpty(); + result.Should().Contain(s => s.Series.Monitored); + result.Should().NotContain(s => !s.Series.Monitored); + } + + [Test] + public void GetMissingForEnabledSeries_should_only_return_explicity_enabled_series_when_backlog_searching_is_ignored() + { + //Setup + var series = Builder.CreateListOfSize(3) + .TheFirst(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Disable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Inherit) + .Build(); + + var episodes = Builder.CreateListOfSize(12) + .TheFirst(3) + .With(e => e.Series = series[0]) + .TheNext(4) + .With(e => e.Series = series[1]) + .TheNext(5) + .With(e => e.Series = series[2]) + .Build(); + + //WithEnableBacklogSearching(); + + Mocker.GetMock() + .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); + + //Act + var result = Mocker.Resolve().GetMissingForEnabledSeries(); + + //Assert + result.Should().NotBeEmpty(); + result.Should().Contain(s => s.Series.BacklogStatus == BacklogStatusType.Enable); + result.Should().NotContain(s => s.Series.BacklogStatus == BacklogStatusType.Disable); + result.Should().NotContain(s => s.Series.BacklogStatus == BacklogStatusType.Inherit); + } + + [Test] + public void GetMissingForEnabledSeries_should_return_explicity_enabled_and_inherit_series_when_backlog_searching_is_enabled() + { + //Setup + var series = Builder.CreateListOfSize(3) + .TheFirst(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Disable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Inherit) + .Build(); + + var episodes = Builder.CreateListOfSize(12) + .TheFirst(3) + .With(e => e.Series = series[0]) + .TheNext(4) + .With(e => e.Series = series[1]) + .TheNext(5) + .With(e => e.Series = series[2]) + .Build(); + + WithEnableBacklogSearching(); + + Mocker.GetMock() + .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); + + //Act + var result = Mocker.Resolve().GetMissingForEnabledSeries(); + + //Assert + result.Should().NotBeEmpty(); + result.Should().Contain(s => s.Series.BacklogStatus == BacklogStatusType.Enable); + result.Should().NotContain(s => s.Series.BacklogStatus == BacklogStatusType.Disable); + result.Should().Contain(s => s.Series.BacklogStatus == BacklogStatusType.Inherit); + } } } diff --git a/NzbDrone.Core.Test/JobTests/RecentBacklogSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/RecentBacklogSearchJobTest.cs index 55f105967..61da40eac 100644 --- a/NzbDrone.Core.Test/JobTests/RecentBacklogSearchJobTest.cs +++ b/NzbDrone.Core.Test/JobTests/RecentBacklogSearchJobTest.cs @@ -3,9 +3,11 @@ using System.Collections.Generic; using System.Linq; using FizzWare.NBuilder; +using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Core.Jobs; +using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; @@ -51,8 +53,15 @@ namespace NzbDrone.Core.Test.JobTests { WithEnableBacklogSearching(); + var series = Builder.CreateNew() + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .Build(); + //Setup var episodes = Builder.CreateListOfSize(50) + .All() + .With(e => e.Series = series) .TheFirst(5) .With(e => e.AirDate = DateTime.Today) .TheNext(5) @@ -87,5 +96,121 @@ namespace NzbDrone.Core.Test.JobTests Mocker.GetMock().Verify(c => c.Start(It.IsAny(), It.IsAny(), 0), Times.Exactly(40)); } + + [Test] + public void GetMissingForEnabledSeries_should_only_return_episodes_for_monitored_series() + { + //Setup + var series = Builder.CreateListOfSize(2) + .TheFirst(1) + .With(s => s.Monitored = false) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .Build(); + + var episodes = Builder.CreateListOfSize(11) + .TheFirst(5) + .With(e => e.Series = series[0]) + .With(e => e.SeasonNumber = 1) + .TheLast(6) + .With(e => e.Series = series[1]) + .Build(); + + WithEnableBacklogSearching(); + + Mocker.GetMock() + .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); + + //Act + var result = Mocker.Resolve().GetMissingForEnabledSeries(); + + //Assert + result.Should().NotBeEmpty(); + result.Should().Contain(s => s.Series.Monitored); + result.Should().NotContain(s => !s.Series.Monitored); + } + + [Test] + public void GetMissingForEnabledSeries_should_only_return_explicity_enabled_series_when_backlog_searching_is_ignored() + { + //Setup + var series = Builder.CreateListOfSize(3) + .TheFirst(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Disable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Inherit) + .Build(); + + var episodes = Builder.CreateListOfSize(12) + .TheFirst(3) + .With(e => e.Series = series[0]) + .TheNext(4) + .With(e => e.Series = series[1]) + .TheNext(5) + .With(e => e.Series = series[2]) + .Build(); + + //WithEnableBacklogSearching(); + + Mocker.GetMock() + .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); + + //Act + var result = Mocker.Resolve().GetMissingForEnabledSeries(); + + //Assert + result.Should().NotBeEmpty(); + result.Should().Contain(s => s.Series.BacklogStatus == BacklogStatusType.Enable); + result.Should().NotContain(s => s.Series.BacklogStatus == BacklogStatusType.Disable); + result.Should().NotContain(s => s.Series.BacklogStatus == BacklogStatusType.Inherit); + } + + [Test] + public void GetMissingForEnabledSeries_should_return_explicity_enabled_and_inherit_series_when_backlog_searching_is_enabled() + { + //Setup + var series = Builder.CreateListOfSize(3) + .TheFirst(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Disable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Enable) + .TheNext(1) + .With(s => s.Monitored = true) + .With(s => s.BacklogStatus = BacklogStatusType.Inherit) + .Build(); + + var episodes = Builder.CreateListOfSize(12) + .TheFirst(3) + .With(e => e.Series = series[0]) + .TheNext(4) + .With(e => e.Series = series[1]) + .TheNext(5) + .With(e => e.Series = series[2]) + .Build(); + + WithEnableBacklogSearching(); + + Mocker.GetMock() + .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); + + //Act + var result = Mocker.Resolve().GetMissingForEnabledSeries(); + + //Assert + result.Should().NotBeEmpty(); + result.Should().Contain(s => s.Series.BacklogStatus == BacklogStatusType.Enable); + result.Should().NotContain(s => s.Series.BacklogStatus == BacklogStatusType.Disable); + result.Should().Contain(s => s.Series.BacklogStatus == BacklogStatusType.Inherit); + } + } } diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120123.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120123.cs new file mode 100644 index 000000000..de038c726 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120123.cs @@ -0,0 +1,15 @@ +using System.Data; +using Migrator.Framework; + +namespace NzbDrone.Core.Datastore.Migrations +{ + [Migration(20120123)] + public class Migration20120123 : NzbDroneMigration + { + protected override void MainDbUpgrade() + { + Database.AddColumn("Series", "BacklogStatus", DbType.Int32, ColumnProperty.Null); + Database.ExecuteNonQuery("UPDATE Series SET BacklogStatus = 2"); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/BacklogSearchJob.cs b/NzbDrone.Core/Jobs/BacklogSearchJob.cs index aca49173a..1029ab4e2 100644 --- a/NzbDrone.Core/Jobs/BacklogSearchJob.cs +++ b/NzbDrone.Core/Jobs/BacklogSearchJob.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; @@ -39,14 +40,7 @@ namespace NzbDrone.Core.Jobs public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { - if (!_configProvider.EnableBacklogSearching) - { - Logger.Trace("Backlog searching is not enabled, aborting job."); - return; - } - - var missingEpisodes = _episodeProvider.EpisodesWithoutFiles(true) - .GroupBy(e => new { e.SeriesId, e.SeasonNumber }); + var missingEpisodes = GetMissingForEnabledSeries().GroupBy(e => new { e.SeriesId, e.SeasonNumber }); var individualEpisodes = new List(); @@ -91,5 +85,26 @@ namespace NzbDrone.Core.Jobs _episodeSearchJob.Start(notification, episode.EpisodeId, 0); } } + + public List GetMissingForEnabledSeries() + { + if (!_configProvider.EnableBacklogSearching) + { + Logger.Trace("Backlog searching is not enabled, only running for explicitly enabled series."); + return _episodeProvider.EpisodesWithoutFiles(true).Where(e => + e.Series.BacklogStatus == BacklogStatusType.Enable && + e.Series.Monitored + ).ToList(); + } + + else + { + Logger.Trace("Backlog searching is enabled, skipping explicity disabled series."); + return _episodeProvider.EpisodesWithoutFiles(true).Where(e => + e.Series.BacklogStatus != BacklogStatusType.Disable && + e.Series.Monitored + ).ToList(); + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/RecentBacklogSearchJob.cs b/NzbDrone.Core/Jobs/RecentBacklogSearchJob.cs index 59830a64c..7714ce0c4 100644 --- a/NzbDrone.Core/Jobs/RecentBacklogSearchJob.cs +++ b/NzbDrone.Core/Jobs/RecentBacklogSearchJob.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; namespace NzbDrone.Core.Jobs { @@ -35,20 +38,35 @@ namespace NzbDrone.Core.Jobs public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { - if (!_configProvider.EnableBacklogSearching) + var missingEpisodes = GetMissingForEnabledSeries(); + + Logger.Debug("Processing missing episodes from the last 30 days, count: {0}", missingEpisodes.Count); + foreach (var episode in missingEpisodes) { - Logger.Trace("Backlog searching is not enabled, aborting job."); - return; + _episodeSearchJob.Start(notification, episode.EpisodeId, 0); } + } - //Get episodes that are considered missing and aired in the last 30 days - var missingEpisodes = _episodeProvider.EpisodesWithoutFiles(true).Where(e => e.AirDate >= DateTime.Today.AddDays(-30)); + public List GetMissingForEnabledSeries() + { + if (!_configProvider.EnableBacklogSearching) + { + Logger.Trace("Backlog searching is not enabled, only running for explicitly enabled series."); + return _episodeProvider.EpisodesWithoutFiles(true).Where(e => + e.AirDate >= DateTime.Today.AddDays(-30) && + e.Series.BacklogStatus == BacklogStatusType.Enable && + e.Series.Monitored + ).ToList(); + } - Logger.Debug("Processing missing episodes from the last 30 days"); - //Process the list of remaining episodes, 1 by 1 - foreach (var episode in missingEpisodes) + else { - _episodeSearchJob.Start(notification, episode.EpisodeId, 0); + Logger.Trace("Backlog searching is enabled, skipping explicity disabled series."); + return _episodeProvider.EpisodesWithoutFiles(true).Where(e => + e.AirDate >= DateTime.Today.AddDays(-30) && + e.Series.BacklogStatus != BacklogStatusType.Disable && + e.Series.Monitored + ).ToList(); } } } diff --git a/NzbDrone.Core/Model/BacklogStatusType.cs b/NzbDrone.Core/Model/BacklogStatusType.cs new file mode 100644 index 000000000..3755333f8 --- /dev/null +++ b/NzbDrone.Core/Model/BacklogStatusType.cs @@ -0,0 +1,9 @@ +namespace NzbDrone.Core.Model +{ + public enum BacklogStatusType + { + Disable = 0, + Enable = 1, + Inherit = 2 + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 39f602725..8d18c6047 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -208,6 +208,7 @@ + @@ -228,6 +229,7 @@ + diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index 86d4060fb..0a63dc0b8 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text.RegularExpressions; using NLog; using NzbDrone.Common; +using NzbDrone.Core.Model; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; @@ -45,7 +46,7 @@ namespace NzbDrone.Core.Providers { var series = _database .Fetch(@"SELECT Series.SeriesId, Series.Title, Series.CleanTitle, Series.Status, Series.Overview, Series.AirsDayOfWeek,Series.AirTimes, - Series.Language, Series.Path, Series.Monitored, Series.QualityProfileId, Series.SeasonFolder, + Series.Language, Series.Path, Series.Monitored, Series.QualityProfileId, Series.SeasonFolder, Series.BacklogStatus, SUM(CASE WHEN Ignored = 0 AND Airdate <= @0 THEN 1 ELSE 0 END) AS EpisodeCount, SUM(CASE WHEN Episodes.Ignored = 0 AND Episodes.EpisodeFileId > 0 AND Episodes.AirDate <= @0 THEN 1 ELSE 0 END) as EpisodeFileCount, MAX(Episodes.SeasonNumber) as SeasonCount, MIN(CASE WHEN AirDate < @0 OR Ignored = 1 THEN NULL ELSE AirDate END) as NextAiring, @@ -55,7 +56,7 @@ namespace NzbDrone.Core.Providers LEFT JOIN Episodes ON Series.SeriesId = Episodes.SeriesId WHERE Series.LastInfoSync IS NOT NULL GROUP BY Series.SeriesId, Series.Title, Series.CleanTitle, Series.Status, Series.Overview, Series.AirsDayOfWeek,Series.AirTimes, - Series.Language, Series.Path, Series.Monitored, Series.QualityProfileId, Series.SeasonFolder, + Series.Language, Series.Path, Series.Monitored, Series.QualityProfileId, Series.SeasonFolder, Series.BacklogStatus, QualityProfiles.QualityProfileId, QualityProfiles.Name, QualityProfiles.Cutoff, QualityProfiles.SonicAllowed", DateTime.Today); return series; @@ -119,6 +120,7 @@ namespace NzbDrone.Core.Providers repoSeries.QualityProfileId = _configProvider.DefaultQualityProfile; repoSeries.SeasonFolder = _configProvider.UseSeasonFolder; + repoSeries.BacklogStatus = BacklogStatusType.Inherit; _database.Insert(repoSeries); } @@ -210,6 +212,7 @@ namespace NzbDrone.Core.Providers series.QualityProfileId = edited.QualityProfileId; series.Monitored = edited.Monitored; series.SeasonFolder = edited.SeasonFolder; + series.BacklogStatus = edited.BacklogStatus; series.Path = edited.Path; } diff --git a/NzbDrone.Core/Repository/Series.cs b/NzbDrone.Core/Repository/Series.cs index 454179487..df9cc5839 100644 --- a/NzbDrone.Core/Repository/Series.cs +++ b/NzbDrone.Core/Repository/Series.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using NzbDrone.Core.Model; using NzbDrone.Core.Repository.Quality; using PetaPoco; @@ -43,6 +44,8 @@ namespace NzbDrone.Core.Repository public bool IsDaily { get; set; } + public BacklogStatusType BacklogStatus { get; set; } + /// /// Gets or sets a value indicating whether this is hidden. /// diff --git a/NzbDrone.Web/Controllers/SeriesController.cs b/NzbDrone.Web/Controllers/SeriesController.cs index cac6ae160..881cd423b 100644 --- a/NzbDrone.Web/Controllers/SeriesController.cs +++ b/NzbDrone.Web/Controllers/SeriesController.cs @@ -4,9 +4,11 @@ using System.IO; using System.Linq; using System.Web.Mvc; using MvcMiniProfiler; +using NzbDrone.Common.Model; using NzbDrone.Core; using NzbDrone.Core.Helpers; using NzbDrone.Core.Jobs; +using NzbDrone.Core.Model; using NzbDrone.Core.Providers; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; @@ -47,6 +49,15 @@ namespace NzbDrone.Web.Controllers var profiles = _qualityProvider.All(); ViewData["SelectList"] = new SelectList(profiles, "QualityProfileId", "Name"); + var backlogStatusTypes = new List>(); + + foreach (BacklogStatusType backlogStatusType in Enum.GetValues(typeof(BacklogStatusType))) + { + backlogStatusTypes.Add(new KeyValuePair((int)backlogStatusType, backlogStatusType.ToString())); + } + + ViewData["BacklogStatusSelectList"] = new SelectList(backlogStatusTypes, "Key", "Value"); + return View(); } @@ -59,13 +70,14 @@ namespace NzbDrone.Web.Controllers [AcceptVerbs(HttpVerbs.Post)] [GridAction] - public ActionResult _SaveAjaxSeriesEditing(int id, string path, bool monitored, bool seasonFolder, int qualityProfileId) + public ActionResult _SaveAjaxSeriesEditing(int id, string path, bool monitored, bool seasonFolder, int qualityProfileId, int backlogStatus) { var oldSeries = _seriesProvider.GetSeries(id); oldSeries.Monitored = monitored; oldSeries.SeasonFolder = seasonFolder; oldSeries.QualityProfileId = qualityProfileId; oldSeries.Path = path; + oldSeries.BacklogStatus = (BacklogStatusType)backlogStatus; _seriesProvider.UpdateSeries(oldSeries); @@ -167,6 +179,19 @@ namespace NzbDrone.Web.Controllers }, "Key", "Value" ); + var backlogStatusTypes = new List>(); + + foreach (BacklogStatusType backlogStatusType in Enum.GetValues(typeof(BacklogStatusType))) + { + backlogStatusTypes.Add(new KeyValuePair((int)backlogStatusType, backlogStatusType.ToString())); + } + + ViewData["BacklogStatusTypes"] = backlogStatusTypes; + + var masterBacklogList = backlogStatusTypes.ToList(); + masterBacklogList.Insert(0, new KeyValuePair(-10, "Unchanged")); + ViewData["MasterBacklogStatusSelectList"] = new SelectList(masterBacklogList, "Key", "Value"); + var series = _seriesProvider.GetAllSeries().OrderBy(o => SortHelper.SkipArticles(o.Title)); return View(series); @@ -196,6 +221,7 @@ namespace NzbDrone.Web.Controllers QualityProfileId = s.QualityProfileId, QualityProfileName = s.QualityProfile.Name, SeasonFolder = s.SeasonFolder, + BacklogStatus = (int)s.BacklogStatus, Status = s.Status, SeasonsCount = s.SeasonCount, EpisodeCount = s.EpisodeCount, diff --git a/NzbDrone.Web/Models/SeriesModel.cs b/NzbDrone.Web/Models/SeriesModel.cs index 0c2774cec..d1b8d5e05 100644 --- a/NzbDrone.Web/Models/SeriesModel.cs +++ b/NzbDrone.Web/Models/SeriesModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Web; +using NzbDrone.Core.Model; using NzbDrone.Core.Repository; namespace NzbDrone.Web.Models @@ -42,5 +43,9 @@ namespace NzbDrone.Web.Models [DisplayName("Monitored")] [Description("Should NzbDrone download episodes for this series?")] public bool Monitored { get; set; } + + [DisplayName("Backlog Status")] + [Description("Should NzbDrone download past missing episodes?")] + public int BacklogStatus { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/Views/Series/EditorTemplates/SeriesModel.cshtml b/NzbDrone.Web/Views/Series/EditorTemplates/SeriesModel.cshtml index 8255a57dc..22c0342ca 100644 --- a/NzbDrone.Web/Views/Series/EditorTemplates/SeriesModel.cshtml +++ b/NzbDrone.Web/Views/Series/EditorTemplates/SeriesModel.cshtml @@ -30,5 +30,9 @@ @Html.DescriptionFor(m => m.Path) @Html.TextBoxFor(m => m.Path, new { @class = "inputClass" }) + + @Html.DropDownListFor(m => m.BacklogStatus, (SelectList)ViewData["BacklogStatusSelectList"], new { @class = "inputClass" }) \ No newline at end of file diff --git a/NzbDrone.Web/Views/Series/MassEdit.cshtml b/NzbDrone.Web/Views/Series/MassEdit.cshtml index 2f3c7666c..9d0444858 100644 --- a/NzbDrone.Web/Views/Series/MassEdit.cshtml +++ b/NzbDrone.Web/Views/Series/MassEdit.cshtml @@ -30,6 +30,10 @@ width: 300px; } + td .backlogStatus { + width: 90px; + } + table { width: 100%; border-width: 1px; @@ -67,6 +71,7 @@ Quality Monitored Season Folder + Backlog Status Path @@ -91,6 +96,10 @@ Should downloaded episodes be stored in season folders? @Html.DropDownList("masterSeasonFolder", (SelectList)ViewData["BoolSelectList"], new { @class = "inputClass" }) + + @Html.DropDownList("masterBacklogStatus", (SelectList)ViewData["MasterBacklogStatusSelectList"], new { @class = "inputClass" }) @@ -118,6 +127,7 @@ var profileId = $('#masterQualitySelector').val(); var monitored = $('#masterMonitored').val(); var seasonFolder = $('#masterSeasonFolder').val(); + var backlogStatus = $('#masterBacklogStatus').val(); var selected = $('.editToggle:checked'); @@ -141,6 +151,10 @@ $(this).parent('td').parent('.seriesEditRow').find('.seasonFolder').prop('checked', seasonFolderBool); } + + if (backlogStatus != -10) { + $(this).parent('td').parent('.seriesEditRow').find('.backlogStatus').val(backlogStatus); + } }); }); diff --git a/NzbDrone.Web/Views/Series/SeriesItem.cshtml b/NzbDrone.Web/Views/Series/SeriesItem.cshtml index 08893f2a9..d52a970a4 100644 --- a/NzbDrone.Web/Views/Series/SeriesItem.cshtml +++ b/NzbDrone.Web/Views/Series/SeriesItem.cshtml @@ -1,4 +1,5 @@ @model NzbDrone.Core.Repository.Series +@using NzbDrone.Core.Model @using NzbDrone.Core.Repository.Quality @using NzbDrone.Web.Helpers @@ -18,6 +19,7 @@ @Html.DropDownListFor(m => m.QualityProfileId, new SelectList((List)ViewData["QualityProfiles"], "QualityProfileId", "Name", Model.QualityProfileId), new { @class = "quality" }) @Html.CheckBoxFor(m => m.Monitored, new {@class = "seriesCheckbox monitored"}) @Html.CheckBoxFor(m => m.SeasonFolder, new {@class = "seriesCheckbox seasonFolder"}) + @Html.DropDownListFor(m => m.BacklogStatus, new SelectList((List>)ViewData["BacklogStatusTypes"], "Key", "Value", (int)Model.BacklogStatus), new { @class = "backlogStatus" }) @Html.TextBoxFor(m => m.Path, new { @class = "path" }) } \ No newline at end of file