diff --git a/NzbDrone.Api/Indexers/ReleaseModule.cs b/NzbDrone.Api/Indexers/ReleaseModule.cs index b8398c7a6..be39a303c 100644 --- a/NzbDrone.Api/Indexers/ReleaseModule.cs +++ b/NzbDrone.Api/Indexers/ReleaseModule.cs @@ -1,10 +1,8 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Api.Mapping; -using NzbDrone.Api.REST; using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.IndexerSearch; using NzbDrone.Core.Indexers; -using NzbDrone.Core.Parser; using Omu.ValueInjecter; using System.Linq; @@ -13,13 +11,32 @@ namespace NzbDrone.Api.Indexers public class ReleaseModule : NzbDroneRestModule { private readonly IFetchAndParseRss _rssFetcherAndParser; + private readonly ISearchForNzb _nzbSearchService; private readonly IMakeDownloadDecision _downloadDecisionMaker; - public ReleaseModule(IFetchAndParseRss rssFetcherAndParser, IMakeDownloadDecision downloadDecisionMaker) + public ReleaseModule(IFetchAndParseRss rssFetcherAndParser, ISearchForNzb nzbSearchService, IMakeDownloadDecision downloadDecisionMaker) { _rssFetcherAndParser = rssFetcherAndParser; + _nzbSearchService = nzbSearchService; _downloadDecisionMaker = downloadDecisionMaker; - GetResourceAll = GetRss; + GetResourceAll = GetReleases; + } + + + private List GetReleases() + { + if (Request.Query.episodeId != null) + { + return GetEpisodeReleases(Request.Query.episodeId); + } + + return GetRss(); + } + + private List GetEpisodeReleases(int episodeId) + { + var decisions = _nzbSearchService.EpisodeSearch(episodeId); + return MapDecisions(decisions); } private List GetRss() @@ -27,6 +44,11 @@ private List GetRss() var reports = _rssFetcherAndParser.Fetch(); var decisions = _downloadDecisionMaker.GetRssDecision(reports); + return MapDecisions(decisions); + } + + private static List MapDecisions(IEnumerable decisions) + { var result = new List(); foreach (var downloadDecision in decisions) @@ -44,24 +66,4 @@ private List GetRss() return result; } } - - public class ReleaseResource : RestResource - { - public Int32 Age { get; set; } - public Int64 Size { get; set; } - public String Indexer { get; set; } - public String NzbInfoUrl { get; set; } - public String NzbUrl { get; set; } - public String ReleaseGroup { get; set; } - public String Title { get; set; } - public Boolean FullSeason { get; set; } - public Boolean SceneSource { get; set; } - public Int32 SeasonNumber { get; set; } - public Language Language { get; set; } - public DateTime? AirDate { get; set; } - public String SeriesTitle { get; set; } - public int[] EpisodeNumbers { get; set; } - public Boolean Approved { get; set; } - public List Rejections { get; set; } - } } \ No newline at end of file diff --git a/NzbDrone.Api/Indexers/ReleaseResource.cs b/NzbDrone.Api/Indexers/ReleaseResource.cs new file mode 100644 index 000000000..fbe121cbb --- /dev/null +++ b/NzbDrone.Api/Indexers/ReleaseResource.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using NzbDrone.Api.REST; +using NzbDrone.Core.Parser; + +namespace NzbDrone.Api.Indexers +{ + public class ReleaseResource : RestResource + { + public Int32 Age { get; set; } + public Int64 Size { get; set; } + public String Indexer { get; set; } + public String NzbInfoUrl { get; set; } + public String NzbUrl { get; set; } + public String ReleaseGroup { get; set; } + public String Title { get; set; } + public Boolean FullSeason { get; set; } + public Boolean SceneSource { get; set; } + public Int32 SeasonNumber { get; set; } + public Language Language { get; set; } + public DateTime? AirDate { get; set; } + public String SeriesTitle { get; set; } + public int[] EpisodeNumbers { get; set; } + public Boolean Approved { get; set; } + public List Rejections { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 8fa07e4eb..92621ce49 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -115,6 +115,7 @@ + diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject b/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject index 033287d89..12a3cf5f9 100644 --- a/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject @@ -26,5 +26,11 @@ NzbDrone\.Common\.Test\.EventingTests\.ServiceNameFixture\..* + + NzbDrone\.Common\.Test\.ProcessProviderTests\..* + + + NzbDrone\.Common\.Test\.ServiceFactoryFixture\..* + \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs b/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs index 1163efe9e..0018be941 100644 --- a/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs +++ b/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs @@ -1,5 +1,6 @@ using System; using System.Text.RegularExpressions; +using NzbDrone.Common.EnsureThat; namespace NzbDrone.Core.IndexerSearch.Definitions { @@ -21,6 +22,8 @@ public string QueryTitle private static string GetQueryTitle(string title) { + Ensure.That(() => title).IsNotNullOrWhiteSpace(); + var cleanTitle = BeginningThe.Replace(title, String.Empty); cleanTitle = cleanTitle diff --git a/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/NzbDrone.Core/IndexerSearch/NzbSearchService.cs index 3cdb8fb0c..c72dd7aa1 100644 --- a/NzbDrone.Core/IndexerSearch/NzbSearchService.cs +++ b/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -14,9 +14,8 @@ namespace NzbDrone.Core.IndexerSearch { public interface ISearchForNzb { - List SearchSingle(int seriesId, int seasonNumber, int episodeNumber); - List SearchDaily(int seriesId, DateTime airDate); - List SearchSeason(int seriesId, int seasonNumber); + List EpisodeSearch(int episodeId); + List SeasonSearch(int seriesId, int seasonNumber); } public class NzbSearchService : ISearchForNzb @@ -40,7 +39,23 @@ public NzbSearchService(IIndexerService indexerService, IFetchFeedFromIndexers f _logger = logger; } - public List SearchSingle(int seriesId, int seasonNumber, int episodeNumber) + + + public List EpisodeSearch(int episodeId) + { + var episode = _episodeService.GetEpisode(episodeId); + var series = _seriesService.GetSeries(episode.SeriesId); + + if (series.SeriesType == SeriesTypes.Daily) + { + return SearchDaily(episode.SeriesId, episode.AirDate.Value.Date); + } + + return SearchSingle(episode.SeriesId, episode.SeasonNumber, episode.EpisodeNumber); + } + + + private List SearchSingle(int seriesId, int seasonNumber, int episodeNumber) { var searchSpec = Get(seriesId, seasonNumber); @@ -59,7 +74,7 @@ public List SearchSingle(int seriesId, int seasonNumber, int e return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); } - public List SearchDaily(int seriesId, DateTime airDate) + private List SearchDaily(int seriesId, DateTime airDate) { var searchSpec = Get(seriesId); searchSpec.Airtime = airDate; @@ -67,7 +82,7 @@ public List SearchDaily(int seriesId, DateTime airDate) return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); } - public List SearchSeason(int seriesId, int seasonNumber) + public List SeasonSearch(int seriesId, int seasonNumber) { var searchSpec = Get(seriesId, seasonNumber); searchSpec.SeasonNumber = seasonNumber; @@ -99,10 +114,15 @@ private List PartialSeasonSearch(SeasonSearchCriteria search) { var spec = new TSpec(); - var tvdbId = _seriesService.GetSeries(seriesId).TvdbId; + var series = _seriesService.GetSeries(seriesId); spec.SeriesId = seriesId; - spec.SceneTitle = _sceneMapping.GetSceneName(tvdbId, seasonNumber); + spec.SceneTitle = _sceneMapping.GetSceneName(series.TvdbId, seasonNumber); + + if (string.IsNullOrWhiteSpace(spec.SceneTitle)) + { + spec.SceneTitle = series.Title; + } return spec; } diff --git a/NzbDrone.Core/IndexerSearch/SearchAndDownloadService.cs b/NzbDrone.Core/IndexerSearch/SearchAndDownloadService.cs index 23f4383a7..193608a5e 100644 --- a/NzbDrone.Core/IndexerSearch/SearchAndDownloadService.cs +++ b/NzbDrone.Core/IndexerSearch/SearchAndDownloadService.cs @@ -12,7 +12,7 @@ interface ISearchAndDownload void SearchSeason(int seriesId, int seasonNumber); } - public class SearchAndDownloadService : ISearchAndDownload + /* public class SearchAndDownloadService : ISearchAndDownload { private readonly ISearchForNzb _searchService; private readonly IMakeDownloadDecision _downloadDecisionMaker; @@ -23,7 +23,7 @@ public SearchAndDownloadService(ISearchForNzb searchService, IMakeDownloadDecisi _downloadDecisionMaker = downloadDecisionMaker; } - public void SearchSingle(int seriesId, int seasonNumber, int episodeNumber) + public void FetchSearchSingle(int seriesId, int seasonNumber, int episodeNumber) { var result = _searchService.SearchSingle(seriesId, seasonNumber, episodeNumber); } @@ -37,5 +37,5 @@ public void SearchSeason(int seriesId, int seasonNumber) { throw new NotImplementedException(); } - } + }*/ } \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/IndexerFetchService.cs b/NzbDrone.Core/Indexers/IndexerFetchService.cs index 82fa37cc1..116ef4a58 100644 --- a/NzbDrone.Core/Indexers/IndexerFetchService.cs +++ b/NzbDrone.Core/Indexers/IndexerFetchService.cs @@ -48,7 +48,7 @@ public IList Fetch(IIndexer indexer, SeasonSearchCriteria searchCrit { _logger.Debug("Searching for {0}", searchCriteria); - var searchUrls = indexer.GetSeasonSearchUrls(searchCriteria.SceneTitle, searchCriteria.SeasonNumber); + var searchUrls = indexer.GetSeasonSearchUrls(searchCriteria.QueryTitle, searchCriteria.SeasonNumber); var result = Fetch(indexer, searchUrls); @@ -60,7 +60,7 @@ public IList Fetch(IIndexer indexer, SingleEpisodeSearchCriteria sea { _logger.Debug("Searching for {0}", searchCriteria); - var searchUrls = indexer.GetEpisodeSearchUrls(searchCriteria.SceneTitle, searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber); + var searchUrls = indexer.GetEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber); var result = Fetch(indexer, searchUrls); @@ -73,7 +73,7 @@ public IList Fetch(IIndexer indexer, PartialSeasonSearchCriteria sea { _logger.Debug("Searching for {0}", searchCriteria); - var searchUrls = indexer.GetSeasonSearchUrls(searchCriteria.SceneTitle, searchCriteria.SeasonNumber); + var searchUrls = indexer.GetSeasonSearchUrls(searchCriteria.QueryTitle, searchCriteria.SeasonNumber); var result = Fetch(indexer, searchUrls); @@ -85,7 +85,7 @@ public IList Fetch(IIndexer indexer, DailyEpisodeSearchCriteria sear { _logger.Debug("Searching for {0}", searchCriteria); - var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchCriteria.SceneTitle, searchCriteria.Airtime); + var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.Airtime); var result = Fetch(indexer, searchUrls); _logger.Info("Finished searching {0} on {1}. Found {2}", indexer.Name, searchCriteria, result.Count); diff --git a/NzbDrone.ncrunchsolution b/NzbDrone.ncrunchsolution index 444b34b1d..969da6dd4 100644 --- a/NzbDrone.ncrunchsolution +++ b/NzbDrone.ncrunchsolution @@ -1,6 +1,6 @@ 1 - False + True true true UseDynamicAnalysis diff --git a/UI/Episode/Layout.js b/UI/Episode/Layout.js index 80cb8cef1..e1864c633 100644 --- a/UI/Episode/Layout.js +++ b/UI/Episode/Layout.js @@ -1,5 +1,5 @@ "use strict"; -define(['app', 'Episode/Summary/View'], function () { +define(['app', 'Shared/SpinnerView', 'Episode/Summary/View', 'Episode/Search/Layout', 'Release/Collection'], function () { NzbDrone.Episode.Layout = Backbone.Marionette.Layout.extend({ template: 'Episode/LayoutTemplate', @@ -27,6 +27,7 @@ define(['app', 'Episode/Summary/View'], function () { onShow: function () { this.showSummary(); + this._releaseSearchActivated = false; }, @@ -53,9 +54,23 @@ define(['app', 'Episode/Summary/View'], function () { e.preventDefault(); } + if (this._releaseSearchActivated) { + return; + } + + var self = this; + this.ui.search.tab('show'); + this.search.show(new NzbDrone.Shared.SpinnerView()); + + var releases = new NzbDrone.Release.Collection(); + var promise = releases.fetchEpisodeReleases(this.model.id); + + promise.done(function () { + self.search.show(new NzbDrone.Episode.Search.Layout({collection: releases})); + }); } }); -}); \ No newline at end of file +}); diff --git a/UI/Episode/Search/Layout.js b/UI/Episode/Search/Layout.js index 4cfbc24e5..dae43d520 100644 --- a/UI/Episode/Search/Layout.js +++ b/UI/Episode/Search/Layout.js @@ -2,9 +2,65 @@ define(['app'], function () { NzbDrone.Episode.Search.Layout = Backbone.Marionette.Layout.extend({ - template: 'Episode/Search/LayoutTemplate' + template: 'Episode/Search/LayoutTemplate', + + regions: { + grid: '#episode-release-grid' + }, + + columns: [ + { + name : 'age', + label : 'Age', + sortable: true, + cell : Backgrid.IntegerCell + }, + { + name : 'size', + label : 'Size', + sortable: true, + cell : Backgrid.IntegerCell + }, + { + name : 'title', + label : 'Title', + sortable: true, + cell : Backgrid.StringCell + }, + { + name : 'seasonNumber', + label: 'season', + cell : Backgrid.IntegerCell + }, + { + name : 'episodeNumber', + label: 'episode', + cell : Backgrid.StringCell + }, + { + name : 'approved', + label: 'Approved', + cell : Backgrid.BooleanCell + } + ], + + initialize: function () { + + }, + + onShow :function(){ + if (!this.isClosed) { + this.grid.show(new Backgrid.Grid( + { + row : Backgrid.Row, + columns : this.columns, + collection: this.collection, + className : 'table table-hover' + })); + } + } }); -}); \ No newline at end of file +}); diff --git a/UI/Episode/Search/LayoutTemplate.html b/UI/Episode/Search/LayoutTemplate.html index 5f282702b..d90cd597d 100644 --- a/UI/Episode/Search/LayoutTemplate.html +++ b/UI/Episode/Search/LayoutTemplate.html @@ -1 +1 @@ - \ No newline at end of file +
diff --git a/UI/Release/Collection.js b/UI/Release/Collection.js index 9ed5a3944..15daf00a8 100644 --- a/UI/Release/Collection.js +++ b/UI/Release/Collection.js @@ -4,6 +4,10 @@ define(['app', 'Release/Model'], function () { url : NzbDrone.Constants.ApiRoot + '/release', model: NzbDrone.Release.Model, - mode : 'client' + mode: 'client', + + fetchEpisodeReleases: function (episodeId) { + return this.fetch({ data: { episodeId: episodeId }}); + } }); }); diff --git a/UI/RouteBinder.js b/UI/RouteBinder.js index 7b5b1e108..5468b0d42 100644 --- a/UI/RouteBinder.js +++ b/UI/RouteBinder.js @@ -10,9 +10,18 @@ define(['app'], function () { $(document).on('click', 'a[href]', this._handleClick); }, + _isInTab: function (element) { + return; + }, + _handleClick: function (event) { var $target = $(event.target); + //check if tab nav + if ($target.parents('.nav-tabs').length) { + return; + } + if ($target.hasClass('no-router')) { return; }