diff --git a/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs index 6c74b4faf..8b527ee20 100644 --- a/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs +++ b/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs @@ -26,7 +26,7 @@ public void SeriesSearch_success() Mocker.GetMock() .Setup(c => c.GetSeasons(1)).Returns(seasons); - Mocker.GetMock() + Mocker.GetMock() .Setup(c => c.IsIgnored(It.IsAny(), It.IsAny())).Returns(false); Mocker.GetMock() diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index b1c461299..d278da6d4 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -117,6 +117,7 @@ + diff --git a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs index 47ff0f5f7..cd8db4ffb 100644 --- a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs @@ -439,7 +439,6 @@ public void RefreshEpisodeInfo_should_add_old_episodes_with_no_title() result.Should().HaveSameCount(fakeEpisodes.Episodes); } - [Test] public void RefreshEpisodeInfo_ignore_season_zero() { @@ -466,6 +465,10 @@ public void RefreshEpisodeInfo_ignore_season_zero() .Setup(c => c.GetSeries(seriesId, true)) .Returns(fakeEpisodes); + Mocker.GetMock() + .Setup(s => s.IsIgnored(seriesId, 0)) + .Returns(true); + //Act Mocker.Resolve().RefreshEpisodeInfo(fakeSeries); @@ -694,6 +697,10 @@ public void RefreshEpisodeInfo_should_ignore_new_episode_for_ignored_season() .Setup(c => c.GetSeries(seriesId, true)) .Returns(tvdbSeries); + Mocker.GetMock() + .Setup(s => s.IsIgnored(seriesId, It.IsAny())) + .Returns(true); + //Act Mocker.Resolve().RefreshEpisodeInfo(fakeSeries); @@ -704,180 +711,6 @@ public void RefreshEpisodeInfo_should_ignore_new_episode_for_ignored_season() result.Where(e => e.Ignored).Should().HaveCount(episodeCount); } - [Test] - public void IsSeasonIgnored_should_return_true_if_all_episodes_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.Ignored = true) - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 2) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 2); - - //Assert - result.Should().BeTrue(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_none_of_episodes_are_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.Ignored = false) - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 2) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 2); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_some_of_episodes_are_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 2) - .With(c => c.Ignored = true) - .Build(); - - episodes[2].Ignored = false; - - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 2); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_zero_episodes_in_db_for_season() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 3) - .With(c => c.Ignored = true) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 2); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_true_if_zero_episodes_in_db_for_season_and_previous_is_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 3) - .With(c => c.Ignored = true) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 4); - - //Assert - result.Should().BeTrue(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_zero_episodes_in_db_for_season_and_previous_is_not_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 3) - .With(c => c.Ignored = false) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 4); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_zero_episodes_in_db_for_season_one() - { - WithRealDb(); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 1); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_true_if_zero_episodes_in_db_for_season_zero() - { - WithRealDb(); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 0); - - //Assert - result.Should().BeTrue(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_season_zero_is_not_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 0) - .With(c => c.Ignored = false) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 0); - - //Assert - result.Should().BeFalse(); - } - [Test] [Explicit] public void Add_daily_show_episodes() @@ -1049,6 +882,10 @@ public void AddEpisode_episode_is_ignored_when_full_season_is_ignored() .With(e => e.Ignored = false) .Build(); + Mocker.GetMock() + .Setup(s => s.IsIgnored(newEpisode.SeriesId, newEpisode.SeasonNumber)) + .Returns(true); + //Act Mocker.Resolve().AddEpisode(newEpisode); @@ -1263,6 +1100,42 @@ public void IgnoreSeason_Ignore_Half() Mocker.VerifyAllMocks(); } + [Test] + public void IgnoreSeason_should_call_SetIgnore_in_season_provider_one_time_only() + { + WithRealDb(); + + var episodes = Builder.CreateListOfSize(4) + .All() + .With(c => c.SeriesId = 10) + .With(c => c.SeasonNumber = 1) + .With(c => c.Ignored = false) + .Build().ToList(); + + var season = new Season + { + SeriesId = 10, + SeasonNumber = 1, + Ignored = false + }; + + Db.Insert(season); + Db.InsertMany(episodes); + + Mocker.GetMock().Setup(s => s.SetIgnore(10, 1, true)).Verifiable(); + + //Act + Mocker.Resolve().SetSeasonIgnore(10, 1, true); + + //Assert + var episodesInDb = Db.Fetch(@"SELECT * FROM Episodes"); + + episodesInDb.Should().HaveCount(4); + episodesInDb.Where(e => e.Ignored).Should().HaveCount(4); + + Mocker.GetMock().Verify(s => s.SetIgnore(10, 1, true), Times.Once()); + } + [Test] public void EpisodesWithoutFiles_no_specials() { diff --git a/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs new file mode 100644 index 000000000..4a2ca931f --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs @@ -0,0 +1,281 @@ +// ReSharper disable RedundantUsingDirective + +using System; +using System.Collections.Generic; +using System.Linq; + +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.AutoMoq; +using PetaPoco; +using TvdbLib.Data; + +namespace NzbDrone.Core.Test.ProviderTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class SeasonProviderTest : CoreTest + { + [Test] + public void AddSeason_should_insert_season_to_database_with_ignored_false() + { + WithRealDb(); + + var seriesId = 10; + var seasonNumber = 50; + + //Act + Mocker.Resolve().Add(seriesId, seasonNumber); + + //Assert + var result = Db.Fetch(); + result.Should().HaveCount(1); + result.First().SeriesId.Should().Be(seriesId); + result.First().SeasonNumber.Should().Be(seasonNumber); + result.First().Ignored.Should().BeFalse(); + } + + [TestCase(true)] + [TestCase(false)] + public void AddSeason_should_insert_season_to_database_with_preset_ignored_status(bool isIgnored) + { + WithRealDb(); + + var seriesId = 10; + var seasonNumber = 50; + + //Act + Mocker.Resolve().Add(seriesId, seasonNumber, isIgnored); + + //Assert + var result = Db.Fetch(); + result.Should().HaveCount(1); + result.First().SeriesId.Should().Be(seriesId); + result.First().SeasonNumber.Should().Be(seasonNumber); + result.First().Ignored.Should().Be(isIgnored); + } + + [Test] + public void DeleteSeason_should_remove_season_from_database() + { + WithRealDb(); + + var fakeSeason = Builder.CreateNew().Build(); + + Db.Insert(fakeSeason); + + //Act + Mocker.Resolve().Delete(fakeSeason.SeriesId, fakeSeason.SeasonNumber); + + //Assert + var result = Db.Fetch(); + result.Should().BeEmpty(); + } + + [Test] + public void SetIgnore_should_update_ignored_status() + { + WithRealDb(); + + var fakeSeason = Builder.CreateNew() + .With(s => s.Ignored = false) + .Build(); + + var id = Db.Insert(fakeSeason); + + //Act + Mocker.Resolve().SetIgnore(fakeSeason.SeriesId, fakeSeason.SeasonNumber, true); + + //Assert + var result = Db.SingleOrDefault(id); + result.Ignored.Should().BeTrue(); + } + + [Test] + public void IsIgnored_should_return_ignored_status_of_season() + { + WithRealDb(); + + //Setup + var fakeSeason = Builder.CreateNew() + .With(s => s.Ignored = false) + .Build(); + + Db.Insert(fakeSeason); + + //Act + var result = Mocker.Resolve().IsIgnored(fakeSeason.SeriesId, fakeSeason.SeasonNumber); + + //Assert + result.Should().Be(fakeSeason.Ignored); + Db.Fetch().Count.Should().Be(1); + } + + [Test] + public void IsIgnored_should_return_true_if_not_in_db_and_is_season_zero() + { + //Setup + WithRealDb(); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 0); + + //Assert + result.Should().BeTrue(); + Db.Fetch().Should().HaveCount(1); + } + + [Test] + public void IsIgnored_should_return_false_if_not_in_db_and_is_season_one() + { + //Setup + WithRealDb(); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 1); + + //Assert + result.Should().BeFalse(); + Db.Fetch().Should().HaveCount(1); + } + + [Test] + public void IsIgnored_should_return_false_if_not_in_db_and_previous_season_is_not_ignored() + { + //Setup + WithRealDb(); + + var lastSeason = Builder.CreateNew() + .With(s => s.SeriesId = 10) + .With(s => s.SeasonNumber = 4) + .With(s => s.Ignored = false) + .Build(); + + Db.Insert(lastSeason); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 5); + + //Assert + result.Should().BeFalse(); + Db.Fetch().Should().HaveCount(2); + } + + [Test] + public void IsIgnored_should_return_true_if_not_in_db_and_previous_season_is_ignored() + { + //Setup + WithRealDb(); + + var lastSeason = Builder.CreateNew() + .With(s => s.SeriesId = 10) + .With(s => s.SeasonNumber = 4) + .With(s => s.Ignored = true) + .Build(); + + Db.Insert(lastSeason); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 5); + + //Assert + result.Should().BeTrue(); + Db.Fetch().Should().HaveCount(2); + } + + [Test] + public void IsIgnored_should_return_false_if_not_in_db_and_previous_season_does_not_exist() + { + //Setup + WithRealDb(); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 5); + + //Assert + result.Should().BeFalse(); + Db.Fetch().Should().HaveCount(1); + } + + [Test] + public void All_should_return_seasons_with_episodes() + { + const int seriesId = 10; + + //Setup + WithRealDb(); + + var season = Builder.CreateNew() + .With(s => s.SeriesId = seriesId) + .With(s => s.SeasonNumber = 4) + .With(s => s.Ignored = true) + .Build(); + + var episodes = Builder.CreateListOfSize(10) + .All() + .With(e => e.SeriesId = seriesId) + .With(e => e.SeasonNumber = season.SeasonNumber) + .Build(); + + Db.Insert(season); + Db.InsertMany(episodes); + + //Act + var result = Mocker.Resolve().All(seriesId); + + //Assert + result.Should().HaveCount(1); + result.First().Episodes.Should().HaveCount(episodes.Count); + } + + [Test] + public void All_should_return_all_seasons_with_episodes() + { + const int seriesId = 10; + + //Setup + WithRealDb(); + + var seasons = Builder.CreateListOfSize(5) + .All() + .With(s => s.SeriesId = seriesId) + .Build(); + + var episodes = new List(); + + for (int i = 0; i < seasons.Count; i++) + { + var newEps = Builder.CreateListOfSize(2) + .All() + .With(e => e.SeriesId = seriesId) + .With(e => e.SeasonNumber = i + 1) + .Build(); + + episodes.AddRange(newEps); + } + + Db.InsertMany(seasons); + Db.InsertMany(episodes); + + //Act + var result = Mocker.Resolve().All(seriesId); + + //Assert + result.Should().HaveCount(5); + + foreach(var season in result) + { + season.Episodes.Count.Should().Be(2); + } + } + } +} diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs new file mode 100644 index 000000000..b9e4d291d --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs @@ -0,0 +1,29 @@ +using System; +using System.Data; +using Migrator.Framework; + +namespace NzbDrone.Core.Datastore.Migrations +{ + + [Migration(20120220)] + public class Migration20120220 : NzbDroneMigration + { + protected override void MainDbUpgrade() + { + Database.AddTable("Seasons", new[] + { + new Column("SeasonId", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity), + new Column("SeriesId", DbType.Int32, ColumnProperty.NotNull), + new Column("SeasonNumber", DbType.Int32, ColumnProperty.NotNull), + new Column("Ignored", DbType.Boolean, ColumnProperty.NotNull) + }); + + Database.ExecuteNonQuery(@"INSERT INTO Seasons (SeriesId, SeasonNumber, Ignored) + SELECT SeriesId, SeasonNumber, + CASE WHEN Count(*) = + SUM(CASE WHEN Ignored = 1 THEN 1 ELSE 0 END) THEN 1 ELSE 0 END AS Ignored + FROM Episodes + GROUP BY SeriesId, SeasonNumber"); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs b/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs new file mode 100644 index 000000000..168e2920b --- /dev/null +++ b/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NzbDrone.Core.Repository; + +namespace NzbDrone.Core.Datastore.PetaPoco +{ + public class EpisodeSeasonRelator + { + public Season _current; + public Season MapIt(Season season, Episode episode) + { + // Terminating call. Since we can return null from this function + // we need to be ready for PetaPoco to callback later with null + // parameters + if (season == null) + return _current; + + // Is this the same season as the current one we're processing + if (_current != null && _current.SeasonId == season.SeasonId) + { + // Yes, just add this post to the current author's collection of posts + _current.Episodes.Add(episode); + + // Return null to indicate we're not done with this author yet + return null; + } + + // This is season different author to the current one, or this is the + // first time through and we don't have an season yet + + // Save the current author + var prev = _current; + + // Setup the new current season + _current = season; + _current.Episodes = new List(); + _current.Episodes.Add(episode); + + // Return the now populated previous season (or null if first time through) + return prev; + } + + } +} diff --git a/NzbDrone.Core/Jobs/SeriesSearchJob.cs b/NzbDrone.Core/Jobs/SeriesSearchJob.cs index 5e225f1dd..6886b4cb4 100644 --- a/NzbDrone.Core/Jobs/SeriesSearchJob.cs +++ b/NzbDrone.Core/Jobs/SeriesSearchJob.cs @@ -8,15 +8,16 @@ namespace NzbDrone.Core.Jobs { public class SeriesSearchJob : IJob { - private readonly EpisodeProvider _episodeProvider; private readonly SeasonSearchJob _seasonSearchJob; + private readonly SeasonProvider _seasonProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - public SeriesSearchJob(EpisodeProvider episodeProvider, SeasonSearchJob seasonSearchJob) + public SeriesSearchJob(SeasonSearchJob seasonSearchJob, + SeasonProvider seasonProvider) { - _episodeProvider = episodeProvider; _seasonSearchJob = seasonSearchJob; + _seasonProvider = seasonProvider; } public string Name @@ -35,12 +36,12 @@ public void Start(ProgressNotification notification, int targetId, int secondary throw new ArgumentOutOfRangeException("targetId"); Logger.Debug("Getting seasons from database for series: {0}", targetId); - var seasons = _episodeProvider.GetSeasons(targetId).Where(s => s > 0); + var seasons = _seasonProvider.GetSeasons(targetId).Where(s => s > 0); foreach (var season in seasons) { //Skip ignored seasons - if (_episodeProvider.IsIgnored(targetId, season)) + if (_seasonProvider.IsIgnored(targetId, season)) continue; _seasonSearchJob.Start(notification, targetId, season); diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index c3344dc16..3790eed2d 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -222,6 +222,7 @@ + @@ -233,6 +234,7 @@ + @@ -267,6 +269,7 @@ + @@ -294,6 +297,7 @@ Code + Code diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index 971b52e99..df0886a6b 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -20,13 +20,16 @@ public class EpisodeProvider private static readonly Regex multiPartCleanupRegex = new Regex(@"\(\d+\)$", RegexOptions.Compiled); private readonly TvDbProvider _tvDbProvider; + private readonly SeasonProvider _seasonProvider; private readonly IDatabase _database; private readonly SeriesProvider _seriesProvider; [Inject] - public EpisodeProvider(IDatabase database, SeriesProvider seriesProvider, TvDbProvider tvDbProviderProvider) + public EpisodeProvider(IDatabase database, SeriesProvider seriesProvider, + TvDbProvider tvDbProviderProvider, SeasonProvider seasonProvider) { _tvDbProvider = tvDbProviderProvider; + _seasonProvider = seasonProvider; _database = database; _seriesProvider = seriesProvider; } @@ -38,8 +41,7 @@ public EpisodeProvider() public virtual void AddEpisode(Episode episode) { //If Season is ignored ignore this episode - if (IsIgnored(episode.SeriesId, episode.SeasonNumber)) - episode.Ignored = true; + episode.Ignored = _seasonProvider.IsIgnored(episode.SeriesId, episode.SeasonNumber); _database.Insert(episode); } @@ -278,7 +280,7 @@ public virtual void RefreshEpisodeInfo(Series series) var updateList = new List(); var newList = new List(); - foreach (var episode in tvDbSeriesInfo.Episodes) + foreach (var episode in tvDbSeriesInfo.Episodes.OrderBy(e => e.SeasonNumber).ThenBy(e => e.EpisodeNumber)) { try { @@ -312,7 +314,7 @@ public virtual void RefreshEpisodeInfo(Series series) //Else we need to check if this episode should be ignored based on IsIgnored rules else { - episodeToUpdate.Ignored = IsIgnored(series.SeriesId, episode.SeasonNumber); + episodeToUpdate.Ignored = _seasonProvider.IsIgnored(series.SeriesId, episode.SeasonNumber); } } else @@ -396,10 +398,14 @@ public virtual IList GetEpisodeNumbersBySeason(int seriesId, int seasonNumb return _database.Fetch("SELECT EpisodeNumber FROM Episodes WHERE SeriesId=@0 AND SeasonNumber=@1", seriesId, seasonNumber).OrderBy(c => c).ToList(); } - public virtual void SetSeasonIgnore(long seriesId, int seasonNumber, bool isIgnored) + public virtual void SetSeasonIgnore(int seriesId, int seasonNumber, bool isIgnored) { logger.Info("Setting ignore flag on Series:{0} Season:{1} to {2}", seriesId, seasonNumber, isIgnored); + //Set the SeasonIgnore + _seasonProvider.SetIgnore(seriesId, seasonNumber, isIgnored); + + //Ignore all the episodes in the season _database.Execute(@"UPDATE Episodes SET Ignored = @0 WHERE SeriesId = @1 AND SeasonNumber = @2 AND Ignored = @3", isIgnored, seriesId, seasonNumber, !isIgnored); diff --git a/NzbDrone.Core/Providers/SeasonProvider.cs b/NzbDrone.Core/Providers/SeasonProvider.cs new file mode 100644 index 000000000..bab22a116 Binary files /dev/null and b/NzbDrone.Core/Providers/SeasonProvider.cs differ diff --git a/NzbDrone.Core/Repository/Season.cs b/NzbDrone.Core/Repository/Season.cs new file mode 100644 index 000000000..ac76d67df --- /dev/null +++ b/NzbDrone.Core/Repository/Season.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using NzbDrone.Core.Model; +using PetaPoco; + +namespace NzbDrone.Core.Repository +{ + [TableName("Seasons")] + [PrimaryKey("SeasonId", autoIncrement = true)] + public class Season + { + public int SeasonId { get; set; } + public int SeriesId { get; set; } + public int SeasonNumber { get; set; } + public Boolean Ignored { get; set; } + + [ResultColumn] + public List Episodes { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/SeriesController.cs b/NzbDrone.Web/Controllers/SeriesController.cs index aedc5d80e..26ca21316 100644 --- a/NzbDrone.Web/Controllers/SeriesController.cs +++ b/NzbDrone.Web/Controllers/SeriesController.cs @@ -24,16 +24,19 @@ public class SeriesController : Controller private readonly QualityProvider _qualityProvider; private readonly SeriesProvider _seriesProvider; private readonly JobProvider _jobProvider; + private readonly SeasonProvider _seasonProvider; // // GET: /Series/ public SeriesController(SeriesProvider seriesProvider, EpisodeProvider episodeProvider, - QualityProvider qualityProvider, JobProvider jobProvider) + QualityProvider qualityProvider, JobProvider jobProvider, + SeasonProvider seasonProvider) { _seriesProvider = seriesProvider; _episodeProvider = episodeProvider; _qualityProvider = qualityProvider; _jobProvider = jobProvider; + _seasonProvider = seasonProvider; } public ActionResult Index() @@ -117,25 +120,14 @@ public ActionResult Details(int seriesId) model.SeriesId = series.SeriesId; model.HasBanner = !String.IsNullOrEmpty(series.BannerUrl); - var seasons = new List(); - var episodes = _episodeProvider.GetEpisodeBySeries(seriesId); - - foreach (var season in episodes.Select(s => s.SeasonNumber).Distinct()) - { - var episodesInSeason = episodes.Where(e => e.SeasonNumber == season).ToList(); - var commonStatusList = episodesInSeason.Select(s => s.Status).Distinct().ToList(); - var commonStatus = commonStatusList.Count > 1 ? "Missing" : commonStatusList.First().ToString(); - - seasons.Add(new SeasonModel - { - SeriesId = seriesId, - SeasonNumber = season, - Episodes = GetEpisodeModels(episodesInSeason).OrderByDescending(e=> e.EpisodeNumber).ToList(), - AnyWanted = episodesInSeason.Any(e => !e.Ignored), - CommonStatus = commonStatus - }); - } - + var seasons = _seasonProvider.All(seriesId).Select(s => new SeasonModel + { + SeriesId = seriesId, + SeasonNumber = s.SeasonNumber, + Ignored = s.Ignored, + Episodes = GetEpisodeModels(s.Episodes).OrderByDescending(e => e.EpisodeNumber).ToList(), + CommonStatus = GetCommonStatus(s.Episodes) + }).ToList(); model.Seasons = seasons; return View(model); @@ -254,5 +246,12 @@ private List GetEpisodeModels(IList episodesInDb) return episodes; } + + private string GetCommonStatus(IList episodes) + { + var commonStatusList = episodes.Select(s => s.Status).Distinct().ToList(); + var commonStatus = commonStatusList.Count > 1 ? "Missing" : commonStatusList.First().ToString(); + return commonStatus; + } } } \ No newline at end of file diff --git a/NzbDrone.Web/Models/SeasonModel.cs b/NzbDrone.Web/Models/SeasonModel.cs index 33816b4d9..904fccb62 100644 --- a/NzbDrone.Web/Models/SeasonModel.cs +++ b/NzbDrone.Web/Models/SeasonModel.cs @@ -9,5 +9,6 @@ public class SeasonModel public List Episodes { get; set; } public bool AnyWanted { get; set; } public string CommonStatus { get; set; } + public bool Ignored { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/Scripts/NzbDrone/seriesDetails.js b/NzbDrone.Web/Scripts/NzbDrone/seriesDetails.js index bdc1b5683..276494086 100644 --- a/NzbDrone.Web/Scripts/NzbDrone/seriesDetails.js +++ b/NzbDrone.Web/Scripts/NzbDrone/seriesDetails.js @@ -40,9 +40,7 @@ $(".ignoreEpisode").live("click", function () { else { //Check to see if this is the last one ignored or the first not ignored - seasonNumber = toggle.attr('class').split(/\s+/)[1].replace('ignoreEpisode_', ''); var episodeId = toggle.attr('id'); - toggleMaster(seasonNumber, ignored); saveEpisodeIgnore(episodeId, ignored); } }); @@ -68,26 +66,6 @@ function toggleChildren(seasonNumber, ignored) { } } -function toggleMaster(seasonNumber) { - //Toggles all master toggles when the childen changes - - var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber); - var ignoredCount = ignoreEpisodes.filter('.ignored').length; - var masters = $('.ignoreSeason_' + seasonNumber); - - masters.each(function (index) { - if (ignoreEpisodes.length == ignoredCount) { - $(this).attr('src', ignoredImage); - $(this).addClass('ignored'); - } - - else { - $(this).attr('src', notIgnoredImage); - $(this).removeClass('ignored'); - } - }); -} - function toggleMasters(seasonNumber, ignored) { //Toggles the other master(s) to match the one that was just changed var masters = $('.ignoreSeason_' + seasonNumber); diff --git a/NzbDrone.Web/Views/Series/Details.cshtml b/NzbDrone.Web/Views/Series/Details.cshtml index c95ca5bcb..0be7f1ed9 100644 --- a/NzbDrone.Web/Views/Series/Details.cshtml +++ b/NzbDrone.Web/Views/Series/Details.cshtml @@ -79,8 +79,8 @@ { var ignoreSeason = "ignoreSeason_" + season.SeasonNumber; diff --git a/NzbDrone.Web/Views/Series/Season.cshtml b/NzbDrone.Web/Views/Series/Season.cshtml index 81c30af32..1e7ff1444 100644 --- a/NzbDrone.Web/Views/Series/Season.cshtml +++ b/NzbDrone.Web/Views/Series/Season.cshtml @@ -22,7 +22,7 @@ @*Commands Column*@ - + Status @Ajax.ImageActionLink("../../Content/Images/Search.png", new { Alt = "Search", Title = "Search for all episodes in this season", @class = "gridImage" }, "SearchSeason", "Episode", new { SeriesId = Model.SeriesId, SeasonNumber = Model.SeasonNumber }, null, null) @Ajax.ImageActionLink("../../Content/Images/Rename.png", new { Alt = "Rename", Title = "Rename all episodes in this season", @class = "gridImage" }, "RenameSeason", "Episode", new { SeriesId = Model.SeriesId, SeasonNumber = Model.SeasonNumber }, null, null)