diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index c708953b9..c1453cd26 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -125,6 +125,7 @@ + diff --git a/NzbDrone.Api/Seasons/SeasonModule.cs b/NzbDrone.Api/Seasons/SeasonModule.cs new file mode 100644 index 000000000..62b69e2fa --- /dev/null +++ b/NzbDrone.Api/Seasons/SeasonModule.cs @@ -0,0 +1,29 @@ +using System.Linq; +using Nancy; +using NzbDrone.Api.Extensions; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Api.Seasons +{ + public class SeasonModule : NzbDroneApiModule + { + private readonly ISeasonService _seasonService; + + public SeasonModule(ISeasonService seasonService) + : base("/Season") + { + _seasonService = seasonService; + + Get["/"] = x => GetSeasons(); + } + + private Response GetSeasons() + { + var seriesId = Request.Query.SeriesId; + + return JsonExtensions.AsResponse(_seasonService.GetSeasonsBySeries(seriesId)); + } + } + + +} \ No newline at end of file diff --git a/NzbDrone.Backbone/NzbDrone.Backbone.csproj b/NzbDrone.Backbone/NzbDrone.Backbone.csproj index e836b6915..c46f1fb9e 100644 --- a/NzbDrone.Backbone/NzbDrone.Backbone.csproj +++ b/NzbDrone.Backbone/NzbDrone.Backbone.csproj @@ -122,10 +122,14 @@ + + + - - + + + diff --git a/NzbDrone.Backbone/Series/Details/SeasonCollection.js b/NzbDrone.Backbone/Series/Details/SeasonCollection.js index 64f998157..848ccfa84 100644 --- a/NzbDrone.Backbone/Series/Details/SeasonCollection.js +++ b/NzbDrone.Backbone/Series/Details/SeasonCollection.js @@ -1,5 +1,6 @@ define(['app','Series/Details/SeasonModel'], function () { NzbDrone.Series.Details.SeasonCollection = Backbone.Collection.extend({ - url: NzbDrone.Constants.ApiRoot + '/season' + url: NzbDrone.Constants.ApiRoot + '/season', + model: NzbDrone.Series.Details.SeasonModel }); }); diff --git a/NzbDrone.Backbone/Series/Details/SeasonCompositeTemplate.html b/NzbDrone.Backbone/Series/Details/SeasonCompositeTemplate.html index cc6edfb31..a42dcd314 100644 --- a/NzbDrone.Backbone/Series/Details/SeasonCompositeTemplate.html +++ b/NzbDrone.Backbone/Series/Details/SeasonCompositeTemplate.html @@ -1,4 +1,4 @@ -

Season {{seasonNumber}}

+

{{seasonTitle}}

diff --git a/NzbDrone.Backbone/Series/Details/SeasonCompositeView.js b/NzbDrone.Backbone/Series/Details/SeasonCompositeView.js index 89ca91e5d..074c26dba 100644 --- a/NzbDrone.Backbone/Series/Details/SeasonCompositeView.js +++ b/NzbDrone.Backbone/Series/Details/SeasonCompositeView.js @@ -6,9 +6,7 @@ define(['app', 'Series/Details/EpisodeItemView'], function () { template: 'Series/Details/SeasonCompositeTemplate', initialize: function() { - var episodes = this.model.get('episodes'); - var test = 1; - //this.collection + } }); }); \ No newline at end of file diff --git a/NzbDrone.Backbone/Series/Details/SeasonModel.js b/NzbDrone.Backbone/Series/Details/SeasonModel.js index 579e20d9e..cde835c86 100644 --- a/NzbDrone.Backbone/Series/Details/SeasonModel.js +++ b/NzbDrone.Backbone/Series/Details/SeasonModel.js @@ -1,4 +1,21 @@ -define(['app', 'Series/Details/SeasonCollection'], function (app) { +define(['app'], function () { NzbDrone.Series.Details.SeasonModel = Backbone.Model.extend({ + + mutators: { + seasonTitle: function () { + var seasonNumber = this.get('seasonNumber'); + + if (seasonNumber === 0) { + return "Specials" + } + + return "Season " + seasonNumber; + } + }, + + defaults: { + seasonNumber: 0 + } }); -}); \ No newline at end of file +}); + diff --git a/NzbDrone.Backbone/Series/Details/SeriesDetailsTemplate.html b/NzbDrone.Backbone/Series/Details/SeriesDetailsTemplate.html index 0bd52c335..d073f2002 100644 --- a/NzbDrone.Backbone/Series/Details/SeriesDetailsTemplate.html +++ b/NzbDrone.Backbone/Series/Details/SeriesDetailsTemplate.html @@ -1,4 +1,6 @@ 
-
+
+ {{overview}} +
\ No newline at end of file diff --git a/NzbDrone.Backbone/Series/Details/SeriesDetailsView.js b/NzbDrone.Backbone/Series/Details/SeriesDetailsView.js index 63dc955c0..da43e8029 100644 --- a/NzbDrone.Backbone/Series/Details/SeriesDetailsView.js +++ b/NzbDrone.Backbone/Series/Details/SeriesDetailsView.js @@ -1,4 +1,4 @@ -define(['app', 'Quality/QualityProfileCollection', 'Series/Details/SeasonCompositeView'], function () { +define(['app', 'Quality/QualityProfileCollection', 'Series/Details/SeasonCompositeView', 'Series/Details/SeasonCollection'], function () { NzbDrone.Series.Details.SeriesDetailsView = Backbone.Marionette.CompositeView.extend({ itemView: NzbDrone.Series.Details.SeasonCompositeView, @@ -6,6 +6,8 @@ define(['app', 'Quality/QualityProfileCollection', 'Series/Details/SeasonComposi template: 'Series/Details/SeriesDetailsTemplate', initialize: function () { + this.collection = new NzbDrone.Series.Details.SeasonCollection(); + this.collection.fetch({data: { seriesId: this.model.get('id') }}); } }); }); \ No newline at end of file diff --git a/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs b/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs index 2f2f19058..d064b635d 100644 --- a/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs +++ b/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Common.Test.EventingTests var intHandler = new Mock>(); - var aggregator = new EventAggregator(TestLogger, new List { intHandler.Object }); + var aggregator = new EventAggregator(TestLogger, () => new List { intHandler.Object }); aggregator.Publish(eventA); intHandler.Verify(c => c.Handle(eventA), Times.Once()); @@ -30,7 +30,7 @@ namespace NzbDrone.Common.Test.EventingTests var intHandler1 = new Mock>(); var intHandler2 = new Mock>(); - var aggregator = new EventAggregator(TestLogger, new List { intHandler1.Object, intHandler2.Object }); + var aggregator = new EventAggregator(TestLogger, () => new List { intHandler1.Object, intHandler2.Object }); aggregator.Publish(eventA); intHandler1.Verify(c => c.Handle(eventA), Times.Once()); @@ -44,7 +44,7 @@ namespace NzbDrone.Common.Test.EventingTests var aHandler = new Mock>(); var bHandler = new Mock>(); - var aggregator = new EventAggregator(TestLogger, new List { aHandler.Object, bHandler.Object }); + var aggregator = new EventAggregator(TestLogger, () => new List { aHandler.Object, bHandler.Object }); aggregator.Publish(eventA); diff --git a/NzbDrone.Common/Eventing/EventAggregator.cs b/NzbDrone.Common/Eventing/EventAggregator.cs index 78fe8c0ad..86d6c901f 100644 --- a/NzbDrone.Common/Eventing/EventAggregator.cs +++ b/NzbDrone.Common/Eventing/EventAggregator.cs @@ -1,18 +1,17 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.EnsureThat; namespace NzbDrone.Common.Eventing { public class EventAggregator : IEventAggregator { private readonly Logger _logger; - private readonly IEnumerable _handlers; + private readonly Func> _handlers; - public EventAggregator(Logger logger, IEnumerable handlers) + public EventAggregator(Logger logger, Func> handlers) { - Ensure.That(() => handlers).HasItems(); _logger = logger; _handlers = handlers; } @@ -21,7 +20,7 @@ namespace NzbDrone.Common.Eventing { _logger.Trace("Publishing {0}", message.GetType().Name); - foreach (var handler in _handlers.OfType>()) + foreach (var handler in _handlers().OfType>()) { _logger.Trace("{0} => {1}", message.GetType().Name, handler.GetType().Name); handler.Handle(message); diff --git a/NzbDrone.Common/Eventing/IEventAggregator.cs b/NzbDrone.Common/Eventing/IEventAggregator.cs index a1369565a..05ca2eba1 100644 --- a/NzbDrone.Common/Eventing/IEventAggregator.cs +++ b/NzbDrone.Common/Eventing/IEventAggregator.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; namespace NzbDrone.Common.Eventing diff --git a/NzbDrone.Core/ContainerExtentions.cs b/NzbDrone.Core/ContainerExtentions.cs index 467c93ae4..66b130e85 100644 --- a/NzbDrone.Core/ContainerExtentions.cs +++ b/NzbDrone.Core/ContainerExtentions.cs @@ -1,10 +1,12 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using Autofac; using Autofac.Core; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Eventing; using NzbDrone.Core.Datastore; using NzbDrone.Core.ExternalNotification; using NzbDrone.Core.Indexers; @@ -25,6 +27,7 @@ namespace NzbDrone.Core containerBuilder.InitDatabase(); + containerBuilder.RegisterModule(); } diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 096d943c8..05c980c44 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -261,6 +261,8 @@ + + diff --git a/NzbDrone.Core/Tv/EpisodeService.cs b/NzbDrone.Core/Tv/EpisodeService.cs index 00ee934e0..06167de0a 100644 --- a/NzbDrone.Core/Tv/EpisodeService.cs +++ b/NzbDrone.Core/Tv/EpisodeService.cs @@ -7,6 +7,7 @@ using NzbDrone.Core.Datastore; using NzbDrone.Core.Download; using NzbDrone.Core.Model; using NzbDrone.Core.Providers; +using NzbDrone.Core.Tv.Events; namespace NzbDrone.Core.Tv { @@ -41,12 +42,14 @@ namespace NzbDrone.Core.Tv private readonly TvDbProvider _tvDbProvider; private readonly ISeasonRepository _seasonRepository; private readonly IEpisodeRepository _episodeRepository; + private readonly IEventAggregator _eventAggregator; - public EpisodeService(TvDbProvider tvDbProviderProvider, ISeasonRepository seasonRepository, IEpisodeRepository episodeRepository) + public EpisodeService(TvDbProvider tvDbProviderProvider, ISeasonRepository seasonRepository, IEpisodeRepository episodeRepository, IEventAggregator eventAggregator) { _tvDbProvider = tvDbProviderProvider; _seasonRepository = seasonRepository; _episodeRepository = episodeRepository; + _eventAggregator = eventAggregator; } public void AddEpisode(Episode episode) @@ -240,7 +243,7 @@ namespace NzbDrone.Core.Tv episodeToUpdate.Overview = episode.Overview; episodeToUpdate.AirDate = episode.AirDate; - if(!String.IsNullOrWhiteSpace(series.AirTime) && episodeToUpdate.AirDate.HasValue) + if (!String.IsNullOrWhiteSpace(series.AirTime) && episodeToUpdate.AirDate.HasValue) { episodeToUpdate.AirDate = episodeToUpdate.AirDate.Value.Add(Convert.ToDateTime(series.AirTime).TimeOfDay) .AddHours(series.UtcOffset * -1); @@ -258,6 +261,16 @@ namespace NzbDrone.Core.Tv _episodeRepository.InsertMany(newList); _episodeRepository.UpdateMany(updateList); + if (newList.Any()) + { + _eventAggregator.Publish(new EpisodeInfoAddedEvent(newList)); + } + + if (updateList.Any()) + { + _eventAggregator.Publish(new EpisodeInfoUpdatedEvent(updateList)); + } + if (failCount != 0) { logger.Info("Finished episode refresh for series: {0}. Successful: {1} - Failed: {2} ", diff --git a/NzbDrone.Core/Tv/Events/EpisodeInfoAddedEvent.cs b/NzbDrone.Core/Tv/Events/EpisodeInfoAddedEvent.cs new file mode 100644 index 000000000..36b52ca8c --- /dev/null +++ b/NzbDrone.Core/Tv/Events/EpisodeInfoAddedEvent.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using NzbDrone.Common.Eventing; + +namespace NzbDrone.Core.Tv.Events +{ + public class EpisodeInfoAddedEvent : IEvent + { + public ReadOnlyCollection Episodes { get; private set; } + + public EpisodeInfoAddedEvent(IList episodes) + { + Episodes = new ReadOnlyCollection(episodes); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Tv/Events/EpisodeInfoUpdatedEvent.cs b/NzbDrone.Core/Tv/Events/EpisodeInfoUpdatedEvent.cs new file mode 100644 index 000000000..fe2df05de --- /dev/null +++ b/NzbDrone.Core/Tv/Events/EpisodeInfoUpdatedEvent.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using NzbDrone.Common.Eventing; + +namespace NzbDrone.Core.Tv.Events +{ + public class EpisodeInfoUpdatedEvent : IEvent + { + public ReadOnlyCollection Episodes { get; private set; } + + public EpisodeInfoUpdatedEvent(IList episodes) + { + Episodes = new ReadOnlyCollection(episodes); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Tv/SeasonService.cs b/NzbDrone.Core/Tv/SeasonService.cs index 7f85bfc15..d26c035a0 100644 Binary files a/NzbDrone.Core/Tv/SeasonService.cs and b/NzbDrone.Core/Tv/SeasonService.cs differ