diff --git a/NzbDrone.Api/AutomapperBootstraper.cs b/NzbDrone.Api/AutomapperBootstraper.cs index 616a5ae77..74060e6aa 100644 --- a/NzbDrone.Api/AutomapperBootstraper.cs +++ b/NzbDrone.Api/AutomapperBootstraper.cs @@ -1,6 +1,7 @@ using System; using AutoMapper; using NzbDrone.Api.Calendar; +using NzbDrone.Api.Episodes; using NzbDrone.Api.QualityProfiles; using NzbDrone.Api.QualityType; using NzbDrone.Api.Resolvers; @@ -48,6 +49,9 @@ namespace NzbDrone.Api .ForMember(dest => dest.EpisodeTitle, opt => opt.MapFrom(src => src.Title)) .ForMember(dest => dest.Start, opt => opt.MapFrom(src => src.AirDate)) .ForMember(dest => dest.End, opt => opt.ResolveUsing()); + + //Episode + Mapper.CreateMap(); } } } \ No newline at end of file diff --git a/NzbDrone.Api/Episodes/EpisodeModule.cs b/NzbDrone.Api/Episodes/EpisodeModule.cs new file mode 100644 index 000000000..290c7e07b --- /dev/null +++ b/NzbDrone.Api/Episodes/EpisodeModule.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using AutoMapper; +using Nancy; +using NzbDrone.Api.Extensions; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Api.Episodes +{ + public class EpisodeModule : NzbDroneApiModule + { + private readonly EpisodeService _episodeService; + + public EpisodeModule(EpisodeService episodeService) + : base("/episodes") + { + _episodeService = episodeService; + Get["/{seriesId}"] = x => GetEpisodesForSeries(x.SeriesId); + } + + private Response GetEpisodesForSeries(int seriesId) + { + var episodes = _episodeService.GetEpisodeBySeries(seriesId); + return Mapper.Map, List>(episodes).AsResponse(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Episodes/EpisodeResource.cs b/NzbDrone.Api/Episodes/EpisodeResource.cs new file mode 100644 index 000000000..95bf3b2e7 --- /dev/null +++ b/NzbDrone.Api/Episodes/EpisodeResource.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Api.Episodes +{ + public class EpisodeResource + { + public Int32 SeriesId { get; set; } + public Int32 EpisodeId { get; set; } + public Int32 EpisodeFileId { get; set; } + public Int32 SeasonNumber { get; set; } + public Int32 EpisodeNumber { get; set; } + public String Title { get; set; } + public DateTime AirDate { get; set; } + public Int32 Status { get; set; } + public String Overview { get; set; } + public EpisodeFile EpisodeFile { get; set; } + } +} diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index d6ce23cba..2199e31d5 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -113,6 +113,8 @@ + + diff --git a/NzbDrone.Backbone/Controller.js b/NzbDrone.Backbone/Controller.js index cdd6f3901..b960af31d 100644 --- a/NzbDrone.Backbone/Controller.js +++ b/NzbDrone.Backbone/Controller.js @@ -1,6 +1,9 @@ -define(['app', 'Shared/ModalRegion', 'AddSeries/AddSeriesLayout', 'Series/SeriesCollectionView', - 'Upcoming/UpcomingCollectionView', 'Calendar/CalendarCollectionView', 'Shared/NotificationView', - 'Shared/NotFoundView', 'MainMenuView', 'HeaderView'], function (app, modalRegion) { +define(['app', 'Shared/ModalRegion', 'AddSeries/AddSeriesLayout', + 'Series/SeriesCollectionView', 'Upcoming/UpcomingCollectionView', + 'Calendar/CalendarCollectionView', 'Shared/NotificationView', + 'Shared/NotFoundView', 'MainMenuView', 'HeaderView', + 'Series/Details/SeriesDetailsView', 'Series/Details/EpisodeCollection'], + function (app, modalRegion) { var controller = Backbone.Marionette.Controller.extend({ @@ -26,6 +29,36 @@ NzbDrone.mainRegion.show(new NzbDrone.Calendar.CalendarCollectionView(this, action, query, calendarCollection)); }, + seriesDetails: function (query) { + this.setTitle('Series Title Goes Here'); +// var seriesModel = new NzbDrone.Series.SeriesModel(); +// seriesModel.fetch(); + + var seriesEpisodes = new NzbDrone.Series.Details.EpisodeCollection({ seriesId: query }); + seriesEpisodes.fetch({ + success: function (collection) { + var seasons = collection.models.groupBy(function(episode){ + var seasonNumber = episode.get('seasonNumber'); + + if (seasonNumber === undefined) + return 0; + + return seasonNumber; + }); + + var seasonCollection = new NzbDrone.Series.Details.SeasonCollection(); + + $.each(seasons, function(index, season){ + seasonCollection.add(new NzbDrone.Series.Details.SeasonModel( + { seasonNumber: index, episodes: season }) + ); + }); + + NzbDrone.mainRegion.show(new NzbDrone.Series.Details.SeriesDetailsView({ collection: seasonCollection })); + } + }); + }, + notFound: function () { this.setTitle('Not Found'); NzbDrone.mainRegion.show(new NzbDrone.Shared.NotFoundView(this)); diff --git a/NzbDrone.Backbone/NzbDrone.Backbone.csproj b/NzbDrone.Backbone/NzbDrone.Backbone.csproj index 78f3b3896..e836b6915 100644 --- a/NzbDrone.Backbone/NzbDrone.Backbone.csproj +++ b/NzbDrone.Backbone/NzbDrone.Backbone.csproj @@ -32,6 +32,7 @@ true + @@ -121,6 +122,12 @@ + + + + + + diff --git a/NzbDrone.Backbone/Routing.js b/NzbDrone.Backbone/Routing.js index cff6979f6..6d8820e92 100644 --- a/NzbDrone.Backbone/Routing.js +++ b/NzbDrone.Backbone/Routing.js @@ -9,6 +9,7 @@ 'series/index': 'series', 'series/add': 'addSeries', 'series/add/:action(/:query)': 'addSeries', + 'series/details/:query': 'seriesDetails', 'upcoming': 'upcoming', 'upcoming/index': 'upcoming', 'calendar': 'calendar', diff --git a/NzbDrone.Backbone/Series/Details/EpisodeCollection.js b/NzbDrone.Backbone/Series/Details/EpisodeCollection.js new file mode 100644 index 000000000..f4cbff92d --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/EpisodeCollection.js @@ -0,0 +1,13 @@ +define(['app', 'Series/Details/EpisodeModel'], function () { + NzbDrone.Series.Details.EpisodeCollection = Backbone.Collection.extend({ + initialize: function(options) { + this.seriesId = options.seriesId; + }, + + url: function(){ + return NzbDrone.Constants.ApiRoot + '/episodes/' + this.seriesId; + }, + + model: NzbDrone.Series.Details.EpisodeModel + }); +}); \ No newline at end of file diff --git a/NzbDrone.Backbone/Series/Details/EpisodeItemTemplate.html b/NzbDrone.Backbone/Series/Details/EpisodeItemTemplate.html new file mode 100644 index 000000000..88490cec6 --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/EpisodeItemTemplate.html @@ -0,0 +1 @@ +{{title}} \ No newline at end of file diff --git a/NzbDrone.Backbone/Series/Details/EpisodeItemView.js b/NzbDrone.Backbone/Series/Details/EpisodeItemView.js new file mode 100644 index 000000000..c9874da5f --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/EpisodeItemView.js @@ -0,0 +1,24 @@ +'use strict'; + +define([ + 'app', + 'Series/Details/SeasonModel' + +], function () { + + NzbDrone.Series.Details.EpisodeItemView = Backbone.Marionette.ItemView.extend({ + template: 'Series/Details/EpisodeItemTemplate', + tagName: 'tr', + + ui: { + + }, + + events: { + + }, + onRender: function () { + NzbDrone.ModelBinder.bind(this.model, this.el); + } + }); +}); diff --git a/NzbDrone.Backbone/Series/Details/EpisodeModel.js b/NzbDrone.Backbone/Series/Details/EpisodeModel.js new file mode 100644 index 000000000..e1635f3f8 --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/EpisodeModel.js @@ -0,0 +1,12 @@ +define(['app'], function (app) { + NzbDrone.Series.Details.EpisodeModel = Backbone.Model.extend({ + + mutators: { + + }, + + defaults: { + seasonNumber: 0 + } + }); +}); diff --git a/NzbDrone.Backbone/Series/Details/SeasonCollection.js b/NzbDrone.Backbone/Series/Details/SeasonCollection.js new file mode 100644 index 000000000..c99fc64cd --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/SeasonCollection.js @@ -0,0 +1,10 @@ +define(['app'], function (app) { + NzbDrone.Series.Details.SeasonCollection = Backbone.Collection.extend({ + // Todo: Why does this throw: "this.model is undefined" - Chnaging to another model fixes it + //model: NzbDrone.Series.Details.SeasonModel, + model: NzbDrone.Series.Details.EpisodeModel, + comparator: function(model) { + return -model.get('seasonNumber'); + } + }); +}); diff --git a/NzbDrone.Backbone/Series/Details/SeasonCollectionTemplate.html b/NzbDrone.Backbone/Series/Details/SeasonCollectionTemplate.html new file mode 100644 index 000000000..cc6edfb31 --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/SeasonCollectionTemplate.html @@ -0,0 +1,13 @@ +

Season {{seasonNumber}}

+ + + + + + + + + + + +
#TitleAir DateQualityControls
\ No newline at end of file diff --git a/NzbDrone.Backbone/Series/Details/SeasonCollectionView.js b/NzbDrone.Backbone/Series/Details/SeasonCollectionView.js new file mode 100644 index 000000000..f3d09e88a --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/SeasonCollectionView.js @@ -0,0 +1,15 @@ +'use strict'; + +define(['app', 'Series/Details/EpisodeItemView'], function (app) { + NzbDrone.Series.Details.SeasonCollectionView = Backbone.Marionette.CompositeView.extend({ + itemView: NzbDrone.Series.Details.EpisodeItemView, + itemViewContainer: '#seasons', + template: 'Series/Details/SeasonCollectionTemplate', + + 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 new file mode 100644 index 000000000..10f189ccc --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/SeasonModel.js @@ -0,0 +1,11 @@ +define(['app', 'Series/Details/SeasonCollection'], function (app) { + NzbDrone.Series.Details.SeasonModel = Backbone.Model.extend({ + //Season Number + //Episodes + + initialize: function(options) { + this.seasonNumber = options.seasonNumber; + this.episodes = options.episodes; + } + }); +}); \ No newline at end of file diff --git a/NzbDrone.Backbone/Series/Details/SeriesDetailsTemplate.html b/NzbDrone.Backbone/Series/Details/SeriesDetailsTemplate.html new file mode 100644 index 000000000..273256ce1 --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/SeriesDetailsTemplate.html @@ -0,0 +1,4 @@ +
+
+
+
\ No newline at end of file diff --git a/NzbDrone.Backbone/Series/Details/SeriesDetailsView.js b/NzbDrone.Backbone/Series/Details/SeriesDetailsView.js new file mode 100644 index 000000000..523e1e0da --- /dev/null +++ b/NzbDrone.Backbone/Series/Details/SeriesDetailsView.js @@ -0,0 +1,19 @@ +define(['app', 'Quality/QualityProfileCollection', 'Series/Details/SeasonCollectionView'], function (app, qualityProfileCollection) { + NzbDrone.Series.Details.SeriesDetailsView = Backbone.Marionette.CompositeView.extend({ + itemView: NzbDrone.Series.Details.SeasonCollectionView, + itemViewContainer: '#seasons', + template: 'Series/Details/SeriesDetailsTemplate', + qualityProfileCollection: qualityProfileCollection, + + initialize: function (options) { + this.collection = options.collection; + + this.qualityProfileCollection.fetch(); + }, + + onCompositeCollectionRendered: function() + { + var test = 1; + } + }); +}); \ No newline at end of file diff --git a/NzbDrone.Backbone/app.js b/NzbDrone.Backbone/app.js index 47b23d665..e5e7b49d9 100644 --- a/NzbDrone.Backbone/app.js +++ b/NzbDrone.Backbone/app.js @@ -32,6 +32,7 @@ define('app', function () { window.NzbDrone.Series = {}; window.NzbDrone.Series.Edit = {}; window.NzbDrone.Series.Delete = {}; + window.NzbDrone.Series.Details = {}; window.NzbDrone.AddSeries = {}; window.NzbDrone.AddSeries.New = {}; window.NzbDrone.AddSeries.Existing = {}; diff --git a/NzbDrone.Core/Tv/EpisodeService.cs b/NzbDrone.Core/Tv/EpisodeService.cs index cca30175e..0b4fcc787 100644 --- a/NzbDrone.Core/Tv/EpisodeService.cs +++ b/NzbDrone.Core/Tv/EpisodeService.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Core.Tv Episode GetEpisode(int id); Episode GetEpisode(int seriesId, int seasonNumber, int episodeNumber); Episode GetEpisode(int seriesId, DateTime date); - IList GetEpisodeBySeries(int seriesId); + List GetEpisodeBySeries(int seriesId); IList GetEpisodesBySeason(int seriesId, int seasonNumber); IList GetEpisodesByParseResult(EpisodeParseResult parseResult); IList EpisodesWithoutFiles(bool includeSpecials); @@ -56,32 +56,32 @@ namespace NzbDrone.Core.Tv _episodeRepository.Insert(episode); } - public virtual Episode GetEpisode(int id) + public Episode GetEpisode(int id) { return _episodeRepository.Get(id); } - public virtual Episode GetEpisode(int seriesId, int seasonNumber, int episodeNumber) + public Episode GetEpisode(int seriesId, int seasonNumber, int episodeNumber) { return _episodeRepository.Get(seriesId, seasonNumber, episodeNumber); } - public virtual Episode GetEpisode(int seriesId, DateTime date) + public Episode GetEpisode(int seriesId, DateTime date) { return _episodeRepository.Get(seriesId, date); } - public virtual IList GetEpisodeBySeries(int seriesId) + public List GetEpisodeBySeries(int seriesId) { - return _episodeRepository.GetEpisodes(seriesId); + return _episodeRepository.GetEpisodes(seriesId).ToList(); } - public virtual IList GetEpisodesBySeason(int seriesId, int seasonNumber) + public IList GetEpisodesBySeason(int seriesId, int seasonNumber) { return _episodeRepository.GetEpisodes(seriesId, seasonNumber); } - public virtual IList GetEpisodesByParseResult(EpisodeParseResult parseResult) + public IList GetEpisodesByParseResult(EpisodeParseResult parseResult) { var result = new List(); @@ -159,22 +159,22 @@ namespace NzbDrone.Core.Tv return result; } - public virtual IList EpisodesWithoutFiles(bool includeSpecials) + public IList EpisodesWithoutFiles(bool includeSpecials) { return _episodeRepository.EpisodesWithoutFiles(includeSpecials); } - public virtual IList GetEpisodesByFileId(int episodeFileId) + public IList GetEpisodesByFileId(int episodeFileId) { return _episodeRepository.GetEpisodeByFileId(episodeFileId); } - public virtual IList EpisodesWithFiles() + public IList EpisodesWithFiles() { return _episodeRepository.EpisodesWithFiles(); } - public virtual void RefreshEpisodeInfo(Series series) + public void RefreshEpisodeInfo(Series series) { logger.Trace("Starting episode info refresh for series: {0}", series.Title.WithDefault(series.Id)); var successCount = 0; @@ -270,17 +270,17 @@ namespace NzbDrone.Core.Tv DeleteEpisodesNotInTvdb(series, tvdbEpisodes); } - public virtual void UpdateEpisode(Episode episode) + public void UpdateEpisode(Episode episode) { _episodeRepository.Update(episode); } - public virtual IList GetEpisodeNumbersBySeason(int seriesId, int seasonNumber) + public IList GetEpisodeNumbersBySeason(int seriesId, int seasonNumber) { return GetEpisodesBySeason(seriesId, seasonNumber).Select(c => c.Id).ToList(); } - public virtual void SetEpisodeIgnore(int episodeId, bool isIgnored) + public void SetEpisodeIgnore(int episodeId, bool isIgnored) { var episode = _episodeRepository.Get(episodeId); _episodeRepository.SetIgnoreFlat(episode, isIgnored); @@ -288,7 +288,7 @@ namespace NzbDrone.Core.Tv logger.Info("Ignore flag for Episode:{0} was set to {1}", episodeId, isIgnored); } - public virtual bool IsFirstOrLastEpisodeOfSeason(int seriesId, int seasonNumber, int episodeNumber) + public bool IsFirstOrLastEpisodeOfSeason(int seriesId, int seasonNumber, int episodeNumber) { var episodes = GetEpisodesBySeason(seriesId, seasonNumber).OrderBy(e => e.EpisodeNumber); @@ -316,7 +316,7 @@ namespace NzbDrone.Core.Tv logger.Trace("Deleted episodes that no longer exist in TVDB for {0}", series.Id); } - public virtual void SetPostDownloadStatus(List episodeIds, PostDownloadStatusType postDownloadStatus) + public void SetPostDownloadStatus(List episodeIds, PostDownloadStatusType postDownloadStatus) { if (episodeIds.Count == 0) throw new ArgumentException("episodeIds should contain one or more episode ids."); @@ -332,12 +332,12 @@ namespace NzbDrone.Core.Tv logger.Trace("Updating PostDownloadStatus for {0} episode(s) to {1}", episodeIds.Count, postDownloadStatus); } - public virtual void UpdateEpisodes(List episodes) + public void UpdateEpisodes(List episodes) { _episodeRepository.UpdateMany(episodes); } - public virtual Episode GetEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber) + public Episode GetEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber) { return _episodeRepository.GetEpisodeBySceneNumbering(seriesId, seasonNumber, episodeNumber); } diff --git a/NzbDrone.Core/Tv/Series.cs b/NzbDrone.Core/Tv/Series.cs index aae84bee6..e415bfd19 100644 --- a/NzbDrone.Core/Tv/Series.cs +++ b/NzbDrone.Core/Tv/Series.cs @@ -53,7 +53,5 @@ namespace NzbDrone.Core.Tv public int EpisodeFileCount { get; set; } public int SeasonCount { get; set; } public DateTime? NextAiring { get; set; } - - public List Episodes { get; set; } } } \ No newline at end of file