From c7f8f57f77a9fdfd2910fbc31ff04b6861e76325 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 19 Apr 2012 23:42:13 -0700 Subject: [PATCH 1/7] Episode searching now stores the results of the tests. --- .../JobTests/SeasonSearchJobTest.cs | 16 +- .../AllowedDownloadSpecificationFixture.cs | 14 +- .../ProcessDailySearchResultsFixture.cs | 22 +-- .../ProcessSearchResultsFixture.cs | 36 +++-- NzbDrone.Core/Jobs/RssSyncJob.cs | 2 +- NzbDrone.Core/Jobs/SeasonSearchJob.cs | 14 +- NzbDrone.Core/Model/ReportRejectionType.cs | 21 +++ NzbDrone.Core/NzbDrone.Core.csproj | 4 + .../AllowedDownloadSpecification.cs | 15 +- NzbDrone.Core/Providers/SearchProvider.cs | 145 ++++++++++++++---- .../Providers/SearchResultProvider.cs | 60 ++++++++ .../Repository/Search/SearchResult.cs | 24 +++ .../Repository/Search/SearchResultItem.cs | 22 +++ 13 files changed, 314 insertions(+), 81 deletions(-) create mode 100644 NzbDrone.Core/Model/ReportRejectionType.cs create mode 100644 NzbDrone.Core/Providers/SearchResultProvider.cs create mode 100644 NzbDrone.Core/Repository/Search/SearchResult.cs create mode 100644 NzbDrone.Core/Repository/Search/SearchResultItem.cs diff --git a/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs index d30e91f8d..ff8775278 100644 --- a/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs +++ b/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs @@ -6,9 +6,11 @@ using FizzWare.NBuilder; 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.Repository; +using NzbDrone.Core.Repository.Search; using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common.AutoMoq; @@ -39,10 +41,16 @@ namespace NzbDrone.Core.Test.JobTests [Test] public void SeasonSearch_partial_season_success() { + var resultItems = Builder.CreateListOfSize(5) + .All() + .With(e => e.SearchError = ReportRejectionType.None) + .With(e => e.Success = true) + .Build(); + var episodes = Builder.CreateListOfSize(5) .All() - .With(e => e.SeriesId = 1) .With(e => e.SeasonNumber = 1) + .With(e => e.SeriesId = 5) .Build(); var notification = new ProgressNotification("Season Search"); @@ -55,7 +63,7 @@ namespace NzbDrone.Core.Test.JobTests Mocker.GetMock() .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) - .Returns(episodes.Select(e => e.EpisodeNumber).ToList()); + .Returns(resultItems.ToList()); //Act Mocker.Resolve().Start(notification, 1, 1); @@ -88,7 +96,7 @@ namespace NzbDrone.Core.Test.JobTests Mocker.GetMock() .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) - .Returns(new List{1}); + .Returns(new List{ new SearchResultItem{ Success = true }}); //Act Mocker.Resolve().Start(notification, 1, 1); @@ -122,7 +130,7 @@ namespace NzbDrone.Core.Test.JobTests Mocker.GetMock() .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) - .Returns(new List()); + .Returns(new List { new SearchResultItem { Success = false, SearchError = ReportRejectionType.Size} }); //Act diff --git a/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/AllowedDownloadSpecificationFixture.cs b/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/AllowedDownloadSpecificationFixture.cs index c33f6c89c..17ff465c9 100644 --- a/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/AllowedDownloadSpecificationFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/AllowedDownloadSpecificationFixture.cs @@ -88,42 +88,42 @@ namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests [Test] public void should_be_allowed_if_all_conditions_are_met() { - spec.IsSatisfiedBy(parseResult).Should().BeTrue(); + spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.None); } [Test] public void should_not_be_allowed_if_profile_is_not_allowed() { WithProfileNotAllowed(); - spec.IsSatisfiedBy(parseResult).Should().BeFalse(); + spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.QualityNotWanted); } [Test] public void should_not_be_allowed_if_size_is_not_allowed() { WithNotAcceptableSize(); - spec.IsSatisfiedBy(parseResult).Should().BeFalse(); + spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.Size); } [Test] public void should_not_be_allowed_if_disk_is_not_upgrade() { WithNoDiskUpgrade(); - spec.IsSatisfiedBy(parseResult).Should().BeFalse(); + spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.ExistingQualityIsEqualOrBetter); } [Test] public void should_not_be_allowed_if_episode_is_already_in_queue() { WithEpisodeAlreadyInQueue(); - spec.IsSatisfiedBy(parseResult).Should().BeFalse(); + spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.AlreadyInQueue); } [Test] public void should_not_be_allowed_if_report_is_over_retention() { WithOverRetention(); - spec.IsSatisfiedBy(parseResult).Should().BeFalse(); + spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.Retention); } [Test] @@ -134,7 +134,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests WithProfileNotAllowed(); WithOverRetention(); - spec.IsSatisfiedBy(parseResult).Should().BeFalse(); + spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.QualityNotWanted); } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessDailySearchResultsFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessDailySearchResultsFixture.cs index 404f8acd0..5c1f6dfb3 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessDailySearchResultsFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessDailySearchResultsFixture.cs @@ -77,14 +77,14 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests { Mocker.GetMock() .Setup(s => s.IsSatisfiedBy(It.IsAny())) - .Returns(true); + .Returns(ReportRejectionType.None); } private void WithQualityNotNeeded() { Mocker.GetMock() .Setup(s => s.IsSatisfiedBy(It.IsAny())) - .Returns(false); + .Returns(ReportRejectionType.ExistingQualityIsEqualOrBetter); } [Test] @@ -103,13 +103,13 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests Mocker.GetMock() .Setup(s => s.IsSatisfiedBy(It.Is(d => d.Quality.QualityType == QualityTypes.Bluray1080p))) - .Returns(true); + .Returns(ReportRejectionType.None); //Act var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); //Assert - result.Should().BeTrue(); + result.Should().Contain(n => n.Success); Mocker.GetMock().Verify(c => c.IsSatisfiedBy(It.IsAny()), Times.Once()); @@ -133,7 +133,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); //Assert - result.Should().BeFalse(); + result.Should().NotContain(n => n.Success); Mocker.GetMock().Verify(c => c.IsSatisfiedBy(It.IsAny()), Times.Exactly(5)); @@ -155,7 +155,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); //Assert - result.Should().BeFalse(); + result.Should().NotContain(n => n.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); @@ -175,7 +175,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); //Assert - result.Should().BeFalse(); + result.Should().NotContain(n => n.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); @@ -198,7 +198,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); //Assert - result.Should().BeTrue(); + result.Should().Contain(n => n.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Once()); @@ -230,7 +230,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); //Assert - result.Should().BeTrue(); + result.Should().Contain(n => n.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Exactly(2)); @@ -250,7 +250,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); //Assert - result.Should().BeFalse(); + result.Should().NotContain(n => n.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); @@ -270,7 +270,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); //Assert - result.Should().BeFalse(); + result.Should().NotContain(n => n.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs index 40d79bce6..72a6544ca 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs @@ -73,14 +73,14 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests { Mocker.GetMock() .Setup(s => s.IsSatisfiedBy(It.IsAny())) - .Returns(true); + .Returns(ReportRejectionType.None); } private void WithQualityNotNeeded() { Mocker.GetMock() .Setup(s => s.IsSatisfiedBy(It.IsAny())) - .Returns(false); + .Returns(ReportRejectionType.ExistingQualityIsEqualOrBetter); } [Test] @@ -102,14 +102,14 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests Mocker.GetMock() .Setup(s => s.IsSatisfiedBy(It.Is(d => d.Quality.QualityType == QualityTypes.Bluray1080p))) - .Returns(true); + .Returns(ReportRejectionType.None); //Act var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); //Assert - result.Should().HaveCount(1); - result.First().Should().Be(1); + result.Should().HaveCount(parseResults.Count); + result.Should().Contain(s => s.Success); Mocker.GetMock().Verify(c => c.IsSatisfiedBy(It.IsAny()), Times.Once()); @@ -135,13 +135,14 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithSuccessfulDownload(); Mocker.GetMock() - .Setup(s => s.IsSatisfiedBy(It.IsAny())).Returns(true); + .Setup(s => s.IsSatisfiedBy(It.IsAny())).Returns(ReportRejectionType.None); //Act var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, 1, 1); //Assert - result.Should().HaveCount(1); + result.Should().HaveCount(parseResults.Count); + result.Should().Contain(s => s.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.Is(d => d.Age != 100)), Times.Never()); @@ -165,7 +166,8 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); //Assert - result.Should().HaveCount(0); + result.Should().HaveCount(parseResults.Count); + result.Should().NotContain(s => s.Success); Mocker.GetMock().Verify(c => c.IsSatisfiedBy(It.IsAny()), Times.Exactly(5)); @@ -188,7 +190,8 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); //Assert - result.Should().HaveCount(0); + result.Should().HaveCount(parseResults.Count); + result.Should().NotContain(s => s.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); @@ -209,7 +212,8 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); //Assert - result.Should().HaveCount(0); + result.Should().HaveCount(parseResults.Count); + result.Should().NotContain(s => s.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); @@ -230,7 +234,8 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); //Assert - result.Should().HaveCount(0); + result.Should().HaveCount(parseResults.Count); + result.Should().NotContain(s => s.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); @@ -251,7 +256,8 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); //Assert - result.Should().HaveCount(0); + result.Should().HaveCount(parseResults.Count); + result.Should().NotContain(s => s.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); @@ -277,7 +283,8 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1); //Assert - result.Should().HaveCount(1); + result.Should().HaveCount(parseResults.Count); + result.Should().Contain(s => s.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Once()); @@ -310,7 +317,8 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1); //Assert - result.Should().HaveCount(1); + result.Should().HaveCount(parseResults.Count); + result.Should().Contain(s => s.Success); Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Exactly(2)); diff --git a/NzbDrone.Core/Jobs/RssSyncJob.cs b/NzbDrone.Core/Jobs/RssSyncJob.cs index fc10aafd5..e663dffee 100644 --- a/NzbDrone.Core/Jobs/RssSyncJob.cs +++ b/NzbDrone.Core/Jobs/RssSyncJob.cs @@ -70,7 +70,7 @@ namespace NzbDrone.Core.Jobs try { if (_isMonitoredEpisodeSpecification.IsSatisfiedBy(episodeParseResult) && - _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult) && + _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult) == ReportRejectionType.None && _upgradeHistorySpecification.IsSatisfiedBy(episodeParseResult)) { _downloadProvider.DownloadReport(episodeParseResult); diff --git a/NzbDrone.Core/Jobs/SeasonSearchJob.cs b/NzbDrone.Core/Jobs/SeasonSearchJob.cs index d30a9396e..1ac305bd7 100644 --- a/NzbDrone.Core/Jobs/SeasonSearchJob.cs +++ b/NzbDrone.Core/Jobs/SeasonSearchJob.cs @@ -60,15 +60,15 @@ namespace NzbDrone.Core.Jobs //Perform a Partial Season Search var addedSeries = _searchProvider.PartialSeasonSearch(notification, targetId, secondaryTargetId); - addedSeries.Distinct().ToList().Sort(); - var episodeNumbers = episodes.Where(w => w.AirDate <= DateTime.Today.AddDays(1)).Select(s => s.EpisodeNumber).ToList(); - episodeNumbers.Sort(); + //addedSeries.Distinct().ToList().Sort(); + //var episodeNumbers = episodes.Where(w => w.AirDate <= DateTime.Today.AddDays(1)).Select(s => s.EpisodeNumber).ToList(); + //episodeNumbers.Sort(); - if (addedSeries.SequenceEqual(episodeNumbers)) - return; + //if (addedSeries.SequenceEqual(episodeNumbers)) + // return; - //Get the list of episodes that weren't downloaded - var missingEpisodes = episodeNumbers.Except(addedSeries).ToList(); + ////Get the list of episodes that weren't downloaded + //var missingEpisodes = episodeNumbers.Except(addedSeries).ToList(); //TODO: do one by one check only when max number of feeds have been returned by the indexer //Only process episodes that is in missing episodes (To ensure we double check if the episode is available) diff --git a/NzbDrone.Core/Model/ReportRejectionType.cs b/NzbDrone.Core/Model/ReportRejectionType.cs new file mode 100644 index 000000000..0d2ea1b46 --- /dev/null +++ b/NzbDrone.Core/Model/ReportRejectionType.cs @@ -0,0 +1,21 @@ +using System.Linq; + +namespace NzbDrone.Core.Model +{ + public enum ReportRejectionType + { + None = 0, + WrongSeries = 1, + QualityNotWanted = 2, + WrongSeason = 3, + WrongEpisode = 3, + Size = 3, + Retention = 3, + ExistingQualityIsEqualOrBetter = 4, + Cutoff = 5, + AlreadyInQueue = 6, + DownloadClientFailure = 7, + Skipped = 8, + Failure, + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 3932bdaf1..a26ab3fca 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -277,6 +277,7 @@ + @@ -304,6 +305,9 @@ Code + + + Code diff --git a/NzbDrone.Core/Providers/DecisionEngine/AllowedDownloadSpecification.cs b/NzbDrone.Core/Providers/DecisionEngine/AllowedDownloadSpecification.cs index 7321c6a4c..c6e437c5f 100644 --- a/NzbDrone.Core/Providers/DecisionEngine/AllowedDownloadSpecification.cs +++ b/NzbDrone.Core/Providers/DecisionEngine/AllowedDownloadSpecification.cs @@ -2,6 +2,7 @@ using NLog; using Ninject; using NzbDrone.Core.Model; +using NzbDrone.Core.Repository.Search; namespace NzbDrone.Core.Providers.DecisionEngine { @@ -30,16 +31,16 @@ namespace NzbDrone.Core.Providers.DecisionEngine { } - public virtual bool IsSatisfiedBy(EpisodeParseResult subject) + public virtual ReportRejectionType IsSatisfiedBy(EpisodeParseResult subject) { - if (!_qualityAllowedByProfileSpecification.IsSatisfiedBy(subject)) return false; - if (!_upgradeDiskSpecification.IsSatisfiedBy(subject)) return false; - if (!_retentionSpecification.IsSatisfiedBy(subject)) return false; - if (!_acceptableSizeSpecification.IsSatisfiedBy(subject)) return false; - if (_alreadyInQueueSpecification.IsSatisfiedBy(subject)) return false; + if (!_qualityAllowedByProfileSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.QualityNotWanted; + if (!_upgradeDiskSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.ExistingQualityIsEqualOrBetter; + if (!_retentionSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.Retention; + if (!_acceptableSizeSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.Size; + if (_alreadyInQueueSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.AlreadyInQueue; logger.Debug("Episode {0} is needed", subject); - return true; + return ReportRejectionType.None; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs index 1e4ea8a5f..324b3a2fe 100644 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ b/NzbDrone.Core/Providers/SearchProvider.cs @@ -8,6 +8,7 @@ using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers.DecisionEngine; using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Search; namespace NzbDrone.Core.Providers { @@ -21,13 +22,15 @@ namespace NzbDrone.Core.Providers private readonly SceneMappingProvider _sceneMappingProvider; private readonly UpgradePossibleSpecification _upgradePossibleSpecification; private readonly AllowedDownloadSpecification _allowedDownloadSpecification; + private readonly SearchResultProvider _searchResultProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); [Inject] public SearchProvider(EpisodeProvider episodeProvider, DownloadProvider downloadProvider, SeriesProvider seriesProvider, IndexerProvider indexerProvider, SceneMappingProvider sceneMappingProvider, - UpgradePossibleSpecification upgradePossibleSpecification, AllowedDownloadSpecification allowedDownloadSpecification) + UpgradePossibleSpecification upgradePossibleSpecification, AllowedDownloadSpecification allowedDownloadSpecification, + SearchResultProvider searchResultProvider) { _episodeProvider = episodeProvider; _downloadProvider = downloadProvider; @@ -36,6 +39,7 @@ namespace NzbDrone.Core.Providers _sceneMappingProvider = sceneMappingProvider; _upgradePossibleSpecification = upgradePossibleSpecification; _allowedDownloadSpecification = allowedDownloadSpecification; + _searchResultProvider = searchResultProvider; } public SearchProvider() @@ -44,13 +48,20 @@ namespace NzbDrone.Core.Providers public virtual bool SeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) { + var searchResult = new SearchResult + { + SearchTime = DateTime.Now, + SeriesId = seriesId, + SeasonNumber = seasonNumber + }; + var series = _seriesProvider.GetSeries(seriesId); if (series == null) { Logger.Error("Unable to find an series {0} in database", seriesId); return false; - } + } //Return false if the series is a daily series (we only support individual episode searching if (series.IsDaily) @@ -80,46 +91,45 @@ namespace NzbDrone.Core.Providers e => e.EpisodeNumbers = episodeNumbers.ToList() ); - var downloadedEpisodes = ProcessSearchResults(notification, reports, series, seasonNumber); + searchResult.SearchResultItems = ProcessSearchResults(notification, reports, series, seasonNumber); - downloadedEpisodes.Sort(); - episodeNumbers.ToList().Sort(); - - //Returns true if the list of downloaded episodes matches the list of episode numbers - //(either a full season release was grabbed or all individual episodes) - return (downloadedEpisodes.SequenceEqual(episodeNumbers)); + return (searchResult.SearchResultItems.Select(s => s.Success).Count() == episodeNumbers.Count); } - public virtual List PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) + public virtual List PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) { - //This method will search for episodes in a season in groups of 10 episodes S01E0, S01E1, S01E2, etc + var searchResult = new SearchResult + { + SearchTime = DateTime.Now, + SeriesId = seriesId, + SeasonNumber = seasonNumber + }; var series = _seriesProvider.GetSeries(seriesId); if (series == null) { Logger.Error("Unable to find an series {0} in database", seriesId); - return new List(); + return new List(); } //Return empty list if the series is a daily series (we only support individual episode searching if (series.IsDaily) - return new List(); + return new List(); notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber); - var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber); - var reports = PerformSearch(notification, series, seasonNumber, episodes); - Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); if (reports.Count == 0) - return new List(); + return new List(); notification.CurrentMessage = "Processing search results"; + searchResult.SearchResultItems = ProcessSearchResults(notification, reports, series, seasonNumber); - return ProcessSearchResults(notification, reports, series, seasonNumber); + _searchResultProvider.Add(searchResult); + return searchResult.SearchResultItems; } public virtual bool EpisodeSearch(ProgressNotification notification, int episodeId) @@ -136,7 +146,7 @@ namespace NzbDrone.Core.Providers if (!_upgradePossibleSpecification.IsSatisfiedBy(episode)) { Logger.Info("Search for {0} was aborted, file in disk meets or exceeds Profile's Cutoff", episode); - notification.CurrentMessage = String.Format("Skipping search for {0}, file you have is already at cutoff", episode); + notification.CurrentMessage = String.Format("Skipping search for {0}, the file you have is already at cutoff", episode); return false; } @@ -145,19 +155,41 @@ namespace NzbDrone.Core.Providers if (episode.Series.IsDaily && !episode.AirDate.HasValue) { Logger.Warn("AirDate is not Valid for: {0}", episode); + notification.CurrentMessage = String.Format("Search for {0} Failed, AirDate is invalid", episode); return false; } + var searchResult = new SearchResult + { + SearchTime = DateTime.Now, + SeriesId = episode.Series.SeriesId + }; + var reports = PerformSearch(notification, episode.Series, episode.SeasonNumber, new List { episode }); Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); notification.CurrentMessage = "Processing search results"; - if (!episode.Series.IsDaily && ProcessSearchResults(notification, reports, episode.Series, episode.SeasonNumber, episode.EpisodeNumber).Count == 1) - return true; + if (episode.Series.IsDaily) + { + searchResult.AirDate = episode.AirDate.Value; + searchResult.SearchResultItems = ProcessSearchResults(notification, reports, episode.Series, episode.AirDate.Value); - if (episode.Series.IsDaily && ProcessSearchResults(notification, reports, episode.Series, episode.AirDate.Value)) + _searchResultProvider.Add(searchResult); + + if (searchResult.SearchResultItems.Any(r => r.Success)) + return true; + + return false; + } + + if (!episode.Series.IsDaily) + { + searchResult.SeasonNumber = episode.SeasonNumber; + searchResult.EpisodeId = episodeId; + ProcessSearchResults(notification, reports, episode.Series, episode.SeasonNumber, episode.EpisodeNumber); return true; + } Logger.Warn("Unable to find {0} in any of indexers.", episode); @@ -170,7 +202,6 @@ namespace NzbDrone.Core.Providers notification.CurrentMessage = String.Format("Sorry, couldn't find you {0} in any of indexers.", episode); } - return false; } @@ -227,9 +258,10 @@ namespace NzbDrone.Core.Providers return reports; } - public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, Series series, int seasonNumber, int? episodeNumber = null) + public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, Series series, int seasonNumber, int? episodeNumber = null) { var successes = new List(); + var items = new List(); foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality).ThenBy(c => c.Age)) { @@ -237,6 +269,14 @@ namespace NzbDrone.Core.Providers { Logger.Trace("Analysing report " + episodeParseResult); + var item = new SearchResultItem + { + ReportTitle = episodeParseResult.OriginalString, + NzbUrl = episodeParseResult.NzbUrl + }; + + items.Add(item); + //Get the matching series episodeParseResult.Series = _seriesProvider.FindSeries(episodeParseResult.CleanTitle); @@ -244,6 +284,7 @@ namespace NzbDrone.Core.Providers if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId) { Logger.Trace("Unexpected series for search: {0}. Skipping.", episodeParseResult.CleanTitle); + item.SearchError = ReportRejectionType.WrongSeries; continue; } @@ -251,6 +292,7 @@ namespace NzbDrone.Core.Providers if (episodeParseResult.SeasonNumber != seasonNumber) { Logger.Trace("Season number does not match searched season number, skipping."); + item.SearchError = ReportRejectionType.WrongSeason; continue; } @@ -258,6 +300,7 @@ namespace NzbDrone.Core.Providers if (episodeNumber.HasValue && !episodeParseResult.EpisodeNumbers.Contains(episodeNumber.Value)) { Logger.Trace("Searched episode number is not contained in post, skipping."); + item.SearchError = ReportRejectionType.WrongEpisode; continue; } @@ -265,10 +308,12 @@ namespace NzbDrone.Core.Providers if (successes.Intersect(episodeParseResult.EpisodeNumbers).Any()) { Logger.Trace("Episode has already been downloaded in this search, skipping."); + item.SearchError = ReportRejectionType.Skipped; continue; } - if (_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult)) + var rejectionType = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult); + if (rejectionType == ReportRejectionType.None) { Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); try @@ -279,12 +324,18 @@ namespace NzbDrone.Core.Providers //Add the list of episode numbers from this release successes.AddRange(episodeParseResult.EpisodeNumbers); + item.Success = true; + } + else + { + item.SearchError = ReportRejectionType.DownloadClientFailure; } } catch (Exception e) { Logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e); notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult); + item.SearchError = ReportRejectionType.DownloadClientFailure; } } } @@ -294,15 +345,32 @@ namespace NzbDrone.Core.Providers } } - return successes; + return items; } - public bool ProcessSearchResults(ProgressNotification notification, IEnumerable reports, Series series, DateTime airDate) + public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, Series series, DateTime airDate) { + var items = new List(); + var skip = false; + foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality)) { try { + var item = new SearchResultItem + { + ReportTitle = episodeParseResult.OriginalString, + NzbUrl = episodeParseResult.NzbUrl + }; + + items.Add(item); + + if (skip) + { + item.SearchError = ReportRejectionType.Skipped; + continue; + } + Logger.Trace("Analysing report " + episodeParseResult); //Get the matching series @@ -310,13 +378,20 @@ namespace NzbDrone.Core.Providers //If series is null or doesn't match the series we're looking for return if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId) + { + item.SearchError = ReportRejectionType.WrongSeries; continue; + } //If parse result doesn't have an air date or it doesn't match passed in airdate, skip the report. if (!episodeParseResult.AirDate.HasValue || episodeParseResult.AirDate.Value.Date != airDate.Date) + { + item.SearchError = ReportRejectionType.WrongEpisode; continue; + } - if (_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult)) + var allowedDownload = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult); + if (allowedDownload == ReportRejectionType.None) { Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); try @@ -327,7 +402,12 @@ namespace NzbDrone.Core.Providers String.Format("{0} - {1} {2} Added to download queue", episodeParseResult.Series.Title, episodeParseResult.AirDate.Value.ToShortDateString(), episodeParseResult.Quality); - return true; + item.Success = true; + skip = true; + } + else + { + item.SearchError = ReportRejectionType.DownloadClientFailure; } } catch (Exception e) @@ -336,13 +416,18 @@ namespace NzbDrone.Core.Providers notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult); } } + else + { + item.SearchError = allowedDownload; + } } catch (Exception e) { Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); } } - return false; + + return items; } private List GetEpisodeNumberPrefixes(IEnumerable episodeNumbers) diff --git a/NzbDrone.Core/Providers/SearchResultProvider.cs b/NzbDrone.Core/Providers/SearchResultProvider.cs new file mode 100644 index 000000000..a4b8472cb --- /dev/null +++ b/NzbDrone.Core/Providers/SearchResultProvider.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NLog; +using Ninject; +using NzbDrone.Core.Repository.Search; +using PetaPoco; + +namespace NzbDrone.Core.Providers +{ + public class SearchResultProvider + { + private readonly IDatabase _database; + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + [Inject] + public SearchResultProvider(IDatabase database) + { + _database = database; + } + + public SearchResultProvider() + { + + } + + public virtual void Add(SearchResult searchResult) + { + logger.Trace("Adding new search result"); + var id = Convert.ToInt32(_database.Insert(searchResult)); + + searchResult.SearchResultItems.ForEach(s => s.Id = id); + logger.Trace("Adding search result items"); + _database.InsertMany(searchResult.SearchResultItems); + } + + public virtual void Delete(int id) + { + logger.Trace("Deleting search result items attached to: {0}", id); + _database.Execute("DELETE FROM SearchResultItems WHERE SearchResultId = @0", id); + + logger.Trace("Deleting search result: {0}", id); + _database.Delete(id); + } + + public virtual List AllSearchResults() + { + return _database.Fetch(); + } + + public virtual SearchResult GetSearchResult(int id) + { + var result = _database.Single(id); + result.SearchResultItems = _database.Fetch("WHERE SearchResultId = @0", id); + + return result; + } + } +} diff --git a/NzbDrone.Core/Repository/Search/SearchResult.cs b/NzbDrone.Core/Repository/Search/SearchResult.cs new file mode 100644 index 000000000..3350895a1 --- /dev/null +++ b/NzbDrone.Core/Repository/Search/SearchResult.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using NzbDrone.Core.Model; +using NzbDrone.Core.Repository.Quality; +using PetaPoco; + +namespace NzbDrone.Core.Repository.Search +{ + [PrimaryKey("Id", autoIncrement = true)] + [TableName("SearchResults")] + public class SearchResult + { + public int Id { get; set; } + public int SeriesId { get; set; } + public int? SeasonNumber { get; set; } + public int? EpisodeId { get; set; } + public DateTime? AirDate { get; set; } + public DateTime SearchTime { get; set; } + + [ResultColumn] + public List SearchResultItems { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Repository/Search/SearchResultItem.cs b/NzbDrone.Core/Repository/Search/SearchResultItem.cs new file mode 100644 index 000000000..94160a6b1 --- /dev/null +++ b/NzbDrone.Core/Repository/Search/SearchResultItem.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using NzbDrone.Core.Model; +using NzbDrone.Core.Repository.Quality; +using PetaPoco; + +namespace NzbDrone.Core.Repository.Search +{ + [PrimaryKey("Id", autoIncrement = true)] + [TableName("SearchResultItems")] + public class SearchResultItem + { + public int Id { get; set; } + public int SearchResultId { get; set; } + public string ReportTitle { get; set; } + public string NzbUrl { get; set; } + public string NzbInfoUrl { get; set; } + public bool Success { get; set; } + public ReportRejectionType SearchError { get; set; } + } +} \ No newline at end of file From 4f005e45c0ff4aa2e7682ff62955569320aa07ab Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 21 Apr 2012 01:16:15 -0700 Subject: [PATCH 2/7] Search Results grid added. --- .../Datastore/Migrations/Migration20120420.cs | 34 +++++++++++++ NzbDrone.Core/Model/ReportRejectionType.cs | 18 +++---- NzbDrone.Core/NzbDrone.Core.csproj | 1 + .../Providers/Indexer/IndexerBase.cs | 2 +- NzbDrone.Core/Providers/SearchProvider.cs | 30 +++++------- .../Providers/SearchResultProvider.cs | 26 +++++++++- .../Repository/Search/SearchResult.cs | 23 ++++++++- .../Repository/Search/SearchResultItem.cs | 1 + NzbDrone.Web/Controllers/LogController.cs | 48 ++++++++++++++++++- NzbDrone.Web/Models/SearchResultsModel.cs | 13 +++++ NzbDrone.Web/NzbDrone.Web.csproj | 4 ++ NzbDrone.Web/Views/Log/SearchResults.cshtml | 22 +++++++++ 12 files changed, 191 insertions(+), 31 deletions(-) create mode 100644 NzbDrone.Core/Datastore/Migrations/Migration20120420.cs create mode 100644 NzbDrone.Web/Models/SearchResultsModel.cs create mode 100644 NzbDrone.Web/Views/Log/SearchResults.cshtml diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs new file mode 100644 index 000000000..62eae990f --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs @@ -0,0 +1,34 @@ +using System.Data; +using Migrator.Framework; + +namespace NzbDrone.Core.Datastore.Migrations +{ + [Migration(20120420)] + public class Migration20120420 : NzbDroneMigration + { + protected override void MainDbUpgrade() + { + Database.AddTable("SearchResults", new[] + { + new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity), + new Column("SeriesId", DbType.Int32, ColumnProperty.NotNull), + new Column("SeasonNumber", DbType.Int32, ColumnProperty.Null), + new Column("EpisodeId", DbType.Int32, ColumnProperty.Null), + new Column("SearchTime", DbType.DateTime, ColumnProperty.NotNull), + new Column("SuccessfulDownload", DbType.Boolean, ColumnProperty.NotNull) + }); + + Database.AddTable("SearchResultItems", new[] + { + new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity), + new Column("SearchResultId", DbType.Int32, ColumnProperty.NotNull), + new Column("ReportTitle", DbType.String, ColumnProperty.NotNull), + new Column("Indexer", DbType.String, ColumnProperty.NotNull), + new Column("NzbUrl", DbType.String, ColumnProperty.NotNull), + new Column("NzbInfoUrl", DbType.String, ColumnProperty.Null), + new Column("Success", DbType.Boolean, ColumnProperty.NotNull), + new Column("SearchError", DbType.Int32, ColumnProperty.NotNull) + }); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Model/ReportRejectionType.cs b/NzbDrone.Core/Model/ReportRejectionType.cs index 0d2ea1b46..867f37565 100644 --- a/NzbDrone.Core/Model/ReportRejectionType.cs +++ b/NzbDrone.Core/Model/ReportRejectionType.cs @@ -8,14 +8,14 @@ namespace NzbDrone.Core.Model WrongSeries = 1, QualityNotWanted = 2, WrongSeason = 3, - WrongEpisode = 3, - Size = 3, - Retention = 3, - ExistingQualityIsEqualOrBetter = 4, - Cutoff = 5, - AlreadyInQueue = 6, - DownloadClientFailure = 7, - Skipped = 8, - Failure, + WrongEpisode = 4, + Size = 5, + Retention = 6, + ExistingQualityIsEqualOrBetter = 7, + Cutoff = 8, + AlreadyInQueue = 9, + DownloadClientFailure = 10, + Skipped = 11, + Failure = 12, } } diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index a26ab3fca..4a65fae54 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -222,6 +222,7 @@ + diff --git a/NzbDrone.Core/Providers/Indexer/IndexerBase.cs b/NzbDrone.Core/Providers/Indexer/IndexerBase.cs index efda27500..d105e7a3e 100644 --- a/NzbDrone.Core/Providers/Indexer/IndexerBase.cs +++ b/NzbDrone.Core/Providers/Indexer/IndexerBase.cs @@ -192,7 +192,6 @@ namespace NzbDrone.Core.Providers.Indexer { parsedEpisode.NzbUrl = NzbDownloadUrl(item); parsedEpisode.Indexer = Name; - parsedEpisode.OriginalString = item.Title.Text; result.Add(parsedEpisode); } } @@ -237,6 +236,7 @@ namespace NzbDrone.Core.Providers.Indexer var title = TitlePreParser(item); var episodeParseResult = Parser.ParseTitle(title); + episodeParseResult.OriginalString = title; if (episodeParseResult != null) episodeParseResult.Age = DateTime.Now.Date.Subtract(item.PublishDate.Date).Days; _logger.Trace("Parsed: {0} from: {1}", episodeParseResult, item.Title.Text); diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs index 324b3a2fe..fd51c6c1d 100644 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ b/NzbDrone.Core/Providers/SearchProvider.cs @@ -172,22 +172,20 @@ namespace NzbDrone.Core.Providers if (episode.Series.IsDaily) { - searchResult.AirDate = episode.AirDate.Value; searchResult.SearchResultItems = ProcessSearchResults(notification, reports, episode.Series, episode.AirDate.Value); - _searchResultProvider.Add(searchResult); if (searchResult.SearchResultItems.Any(r => r.Success)) return true; - - return false; } - if (!episode.Series.IsDaily) + else { - searchResult.SeasonNumber = episode.SeasonNumber; searchResult.EpisodeId = episodeId; - ProcessSearchResults(notification, reports, episode.Series, episode.SeasonNumber, episode.EpisodeNumber); + searchResult.SearchResultItems = ProcessSearchResults(notification, reports, episode.Series, episode.SeasonNumber, episode.EpisodeNumber); + _searchResultProvider.Add(searchResult); + + if (searchResult.SearchResultItems.Any(r => r.Success)) return true; } @@ -272,7 +270,8 @@ namespace NzbDrone.Core.Providers var item = new SearchResultItem { ReportTitle = episodeParseResult.OriginalString, - NzbUrl = episodeParseResult.NzbUrl + NzbUrl = episodeParseResult.NzbUrl, + Indexer = episodeParseResult.Indexer }; items.Add(item); @@ -312,8 +311,8 @@ namespace NzbDrone.Core.Providers continue; } - var rejectionType = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult); - if (rejectionType == ReportRejectionType.None) + item.SearchError = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult); + if (item.SearchError == ReportRejectionType.None) { Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); try @@ -360,7 +359,8 @@ namespace NzbDrone.Core.Providers var item = new SearchResultItem { ReportTitle = episodeParseResult.OriginalString, - NzbUrl = episodeParseResult.NzbUrl + NzbUrl = episodeParseResult.NzbUrl, + Indexer = episodeParseResult.Indexer }; items.Add(item); @@ -390,8 +390,8 @@ namespace NzbDrone.Core.Providers continue; } - var allowedDownload = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult); - if (allowedDownload == ReportRejectionType.None) + item.SearchError = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult); + if (item.SearchError == ReportRejectionType.None) { Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); try @@ -416,10 +416,6 @@ namespace NzbDrone.Core.Providers notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult); } } - else - { - item.SearchError = allowedDownload; - } } catch (Exception e) { diff --git a/NzbDrone.Core/Providers/SearchResultProvider.cs b/NzbDrone.Core/Providers/SearchResultProvider.cs index a4b8472cb..cba3dc697 100644 --- a/NzbDrone.Core/Providers/SearchResultProvider.cs +++ b/NzbDrone.Core/Providers/SearchResultProvider.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using NLog; using Ninject; +using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Search; using PetaPoco; @@ -28,9 +29,10 @@ namespace NzbDrone.Core.Providers public virtual void Add(SearchResult searchResult) { logger.Trace("Adding new search result"); + searchResult.SuccessfulDownload = searchResult.SearchResultItems.Any(s => s.Success); var id = Convert.ToInt32(_database.Insert(searchResult)); - searchResult.SearchResultItems.ForEach(s => s.Id = id); + searchResult.SearchResultItems.ForEach(s => s.SearchResultId = id); logger.Trace("Adding search result items"); _database.InsertMany(searchResult.SearchResultItems); } @@ -46,7 +48,27 @@ namespace NzbDrone.Core.Providers public virtual List AllSearchResults() { - return _database.Fetch(); + var sql = @"SELECT SearchResults.Id, SearchResults.SeriesId, SearchResults.SeasonNumber, + SearchResults.EpisodeId, SearchResults.SearchTime, + Series.Title as SeriesTitle, Series.IsDaily, + Episodes.EpisodeNumber, Episodes.SeasonNumber, Episodes.Title as EpisodeTitle, + Episodes.AirDate, + Count(SearchResultItems.Id) as TotalItems, + SUM(CASE WHEN SearchResultItems.Success = 1 THEN 1 ELSE 0 END) as Successes + FROM SearchResults + INNER JOIN Series + ON Series.SeriesId = SearchResults.SeriesId + LEFT JOIN Episodes + ON Episodes.EpisodeId = SearchResults.EpisodeId + INNER JOIN SearchResultItems + ON SearchResultItems.SearchResultId = SearchResults.Id + GROUP BY SearchResults.Id, SearchResults.SeriesId, SearchResults.SeasonNumber, + SearchResults.EpisodeId, SearchResults.SearchTime, + Series.Title, Series.IsDaily, + Episodes.EpisodeNumber, Episodes.SeasonNumber, Episodes.Title, + Episodes.AirDate"; + + return _database.Fetch(sql); } public virtual SearchResult GetSearchResult(int id) diff --git a/NzbDrone.Core/Repository/Search/SearchResult.cs b/NzbDrone.Core/Repository/Search/SearchResult.cs index 3350895a1..50ef04550 100644 --- a/NzbDrone.Core/Repository/Search/SearchResult.cs +++ b/NzbDrone.Core/Repository/Search/SearchResult.cs @@ -15,10 +15,31 @@ namespace NzbDrone.Core.Repository.Search public int SeriesId { get; set; } public int? SeasonNumber { get; set; } public int? EpisodeId { get; set; } - public DateTime? AirDate { get; set; } public DateTime SearchTime { get; set; } + public bool SuccessfulDownload { get; set; } [ResultColumn] public List SearchResultItems { get; set; } + + [ResultColumn] + public string SeriesTitle { get; set; } + + [ResultColumn] + public bool IsDaily { get; set; } + + [ResultColumn] + public int? EpisodeNumber { get; set; } + + [ResultColumn] + public string EpisodeTitle { get; set; } + + [ResultColumn] + public DateTime AirDate { get; set; } + + [ResultColumn] + public int TotalItems { get; set; } + + [ResultColumn] + public int Successes { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Repository/Search/SearchResultItem.cs b/NzbDrone.Core/Repository/Search/SearchResultItem.cs index 94160a6b1..51b744e59 100644 --- a/NzbDrone.Core/Repository/Search/SearchResultItem.cs +++ b/NzbDrone.Core/Repository/Search/SearchResultItem.cs @@ -14,6 +14,7 @@ namespace NzbDrone.Core.Repository.Search public int Id { get; set; } public int SearchResultId { get; set; } public string ReportTitle { get; set; } + public string Indexer { get; set; } public string NzbUrl { get; set; } public string NzbInfoUrl { get; set; } public bool Success { get; set; } diff --git a/NzbDrone.Web/Controllers/LogController.cs b/NzbDrone.Web/Controllers/LogController.cs index 60f9668de..5db86794c 100644 --- a/NzbDrone.Web/Controllers/LogController.cs +++ b/NzbDrone.Web/Controllers/LogController.cs @@ -7,6 +7,8 @@ using System.Web.Mvc; using DataTables.Mvc.Core.Models; using NzbDrone.Common; using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository.Search; using NzbDrone.Web.Models; namespace NzbDrone.Web.Controllers @@ -16,12 +18,15 @@ namespace NzbDrone.Web.Controllers private readonly LogProvider _logProvider; private readonly EnvironmentProvider _environmentProvider; private readonly DiskProvider _diskProvider; + private readonly SearchResultProvider _searchResultProvider; - public LogController(LogProvider logProvider, EnvironmentProvider environmentProvider, DiskProvider diskProvider) + public LogController(LogProvider logProvider, EnvironmentProvider environmentProvider, + DiskProvider diskProvider, SearchResultProvider searchResultProvider) { _logProvider = logProvider; _environmentProvider = environmentProvider; _diskProvider = diskProvider; + _searchResultProvider = searchResultProvider; } public ActionResult Index() @@ -50,6 +55,28 @@ namespace NzbDrone.Web.Controllers return JsonNotificationResult.Info("Logs Cleared"); } + public ActionResult SearchResults() + { + var results = _searchResultProvider.AllSearchResults(); + + var model = results.Select(s => new SearchResultsModel + { + Id = s.Id, + SearchTime = s.SearchTime.ToString(), + DisplayName = GetDisplayName(s), + ReportCount = s.TotalItems, + Successful = s.Successes > 0 + }); + + return View(model); + } + + public ActionResult SearchDetails(int searchId) + { + var model = _searchResultProvider.GetSearchResult(searchId); + return View(model); + } + public ActionResult AjaxBinding(DataTablesParams dataTablesParams) { var logs = _logProvider.GetAllLogs(); @@ -102,5 +129,24 @@ namespace NzbDrone.Web.Controllers }, JsonRequestBehavior.AllowGet); } + + public string GetDisplayName(SearchResult searchResult) + { + if (!searchResult.EpisodeNumber.HasValue) + { + return String.Format("{0} - Season {1}", searchResult.SeriesTitle, searchResult.SeasonNumber); + } + + string episodeString; + + if (searchResult.IsDaily) + episodeString = searchResult.AirDate.ToShortDateString().Replace('/', '-'); + + else + episodeString = String.Format("S{0:00}E{1:00}", searchResult.SeasonNumber, + searchResult.EpisodeNumber); + + return String.Format("{0} - {1} - {2}", searchResult.SeriesTitle, episodeString, searchResult.EpisodeTitle); + } } } \ No newline at end of file diff --git a/NzbDrone.Web/Models/SearchResultsModel.cs b/NzbDrone.Web/Models/SearchResultsModel.cs new file mode 100644 index 000000000..178844c1f --- /dev/null +++ b/NzbDrone.Web/Models/SearchResultsModel.cs @@ -0,0 +1,13 @@ +using System; + +namespace NzbDrone.Web.Models +{ + public class SearchResultsModel + { + public int Id { get; set; } + public string DisplayName { get; set; } + public string SearchTime { get; set; } + public int ReportCount { get; set; } + public bool Successful { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index 1d77ee308..778b6edcf 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -237,6 +237,7 @@ + @@ -519,6 +520,9 @@ + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/NzbDrone.Web/Views/Log/SearchResults.cshtml b/NzbDrone.Web/Views/Log/SearchResults.cshtml new file mode 100644 index 000000000..24ac7cadb --- /dev/null +++ b/NzbDrone.Web/Views/Log/SearchResults.cshtml @@ -0,0 +1,22 @@ +@using DataTables.Mvc.Core +@model IEnumerable + +@{ + ViewBag.Title = "Search Results"; +} + +@Html.GridHtml("searchResultsGrid", "dataTablesGrid") + +@section Scripts +{ + @( + Html.GridScriptForModel("#searchResultsGrid") + .PageLength(20) + .ChangePageLength(false) + .AddColumn(new Column().DataProperty("DisplayName").Link("SearchDetails?searchId={Id}", "{DisplayName}").Title("Name")) + .AddColumn(new Column().DataProperty("SearchTime").Title("Time").Width("170px")) + .AddColumn(new Column().DataProperty("ReportCount").Title("Reports Found").Width("140px")) + .AddColumn(new Column().DataProperty("Successful").Title("Successful").Width("110px")) + .AddSorting(1) + ) +} From e7c353b9a32de7e0f103510a94b9d925615e90a7 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 21 Apr 2012 01:25:42 -0700 Subject: [PATCH 3/7] Removed details from searchResultsGrid. --- NzbDrone.Web/Views/Log/SearchResults.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NzbDrone.Web/Views/Log/SearchResults.cshtml b/NzbDrone.Web/Views/Log/SearchResults.cshtml index 24ac7cadb..4c1b9057c 100644 --- a/NzbDrone.Web/Views/Log/SearchResults.cshtml +++ b/NzbDrone.Web/Views/Log/SearchResults.cshtml @@ -5,7 +5,7 @@ ViewBag.Title = "Search Results"; } -@Html.GridHtml("searchResultsGrid", "dataTablesGrid") +@Html.GridHtml("searchResultsGrid", "dataTablesGrid no-details") @section Scripts { From aa24e4cac759f0555ce34d3c6e223bd92cb35878 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 22 Apr 2012 11:53:00 -0700 Subject: [PATCH 4/7] Use Long polling only for signalr. --- NzbDrone.Web/Scripts/NzbDrone/grid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NzbDrone.Web/Scripts/NzbDrone/grid.js b/NzbDrone.Web/Scripts/NzbDrone/grid.js index e2fc1f040..50c4c74fb 100644 --- a/NzbDrone.Web/Scripts/NzbDrone/grid.js +++ b/NzbDrone.Web/Scripts/NzbDrone/grid.js @@ -92,5 +92,5 @@ $(function () { }; // Start the connection - $.connection.hub.start(); + $.connection.hub.start({ transport: 'longPolling' }); }); \ No newline at end of file From cef7b6a8dc1351eb5decd104e52d4bbdbf0907aa Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 22 Apr 2012 23:31:11 -0700 Subject: [PATCH 5/7] SearchResult Controller added. Force Download added. --- .../JobTests/SeasonSearchJobTest.cs | 6 +- .../ProcessSearchResultsFixture.cs | 19 ++-- .../Datastore/Migrations/Migration20120420.cs | 7 +- NzbDrone.Core/Fluent.cs | 55 +++++++++++ NzbDrone.Core/Helpers/FileSizeFormatHelper.cs | 46 --------- NzbDrone.Core/NzbDrone.Core.csproj | 1 - NzbDrone.Core/Providers/SearchProvider.cs | 36 ++++--- .../Providers/SearchResultProvider.cs | 37 ++++++- .../Repository/Search/SearchResult.cs | 5 +- .../Repository/Search/SearchResultItem.cs | 10 ++ NzbDrone.Web/Controllers/LogController.cs | 48 +-------- .../Controllers/SearchResultController.cs | 93 ++++++++++++++++++ NzbDrone.Web/Controllers/SystemController.cs | 3 +- NzbDrone.Web/Models/SearchDetailsModel.cs | 13 +++ NzbDrone.Web/Models/SearchItemModel.cs | 23 +++++ NzbDrone.Web/NzbDrone.Web.csproj | 11 ++- .../Views/SearchResult/Details.cshtml | 45 +++++++++ .../Index.cshtml} | 4 +- NzbDrone.Web/packages.config | 2 +- .../DataTables.Mvc.0.1.0.54.nupkg | Bin 13236 -> 0 bytes .../lib/DataTables.Mvc.Core.dll | Bin 18432 -> 0 bytes .../Content/App_Start/DataTablesMvc.cs.pp | 17 ++++ .../DataTables.Mvc.0.1.0.67.nupkg | Bin 0 -> 13931 bytes .../lib/DataTables.Mvc.Core.dll | Bin 0 -> 19968 bytes .../DataTables.Mvc.0.1.0.67/tools/install.ps1 | 5 + 25 files changed, 358 insertions(+), 128 deletions(-) delete mode 100644 NzbDrone.Core/Helpers/FileSizeFormatHelper.cs create mode 100644 NzbDrone.Web/Controllers/SearchResultController.cs create mode 100644 NzbDrone.Web/Models/SearchDetailsModel.cs create mode 100644 NzbDrone.Web/Models/SearchItemModel.cs create mode 100644 NzbDrone.Web/Views/SearchResult/Details.cshtml rename NzbDrone.Web/Views/{Log/SearchResults.cshtml => SearchResult/Index.cshtml} (84%) delete mode 100644 packages/DataTables.Mvc.0.1.0.54/DataTables.Mvc.0.1.0.54.nupkg delete mode 100644 packages/DataTables.Mvc.0.1.0.54/lib/DataTables.Mvc.Core.dll create mode 100644 packages/DataTables.Mvc.0.1.0.67/Content/App_Start/DataTablesMvc.cs.pp create mode 100644 packages/DataTables.Mvc.0.1.0.67/DataTables.Mvc.0.1.0.67.nupkg create mode 100644 packages/DataTables.Mvc.0.1.0.67/lib/DataTables.Mvc.Core.dll create mode 100644 packages/DataTables.Mvc.0.1.0.67/tools/install.ps1 diff --git a/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs index ff8775278..bb5eae2e1 100644 --- a/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs +++ b/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.JobTests Mocker.GetMock() .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) - .Returns(resultItems.ToList()); + .Returns(episodes.Select(e => e.EpisodeNumber).ToList()); //Act Mocker.Resolve().Start(notification, 1, 1); @@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.JobTests Mocker.GetMock() .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) - .Returns(new List{ new SearchResultItem{ Success = true }}); + .Returns(new List()); //Act Mocker.Resolve().Start(notification, 1, 1); @@ -130,7 +130,7 @@ namespace NzbDrone.Core.Test.JobTests Mocker.GetMock() .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) - .Returns(new List { new SearchResultItem { Success = false, SearchError = ReportRejectionType.Size} }); + .Returns(new List()); //Act diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs index 72a6544ca..40c6022e4 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs @@ -11,6 +11,7 @@ using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.DecisionEngine; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Repository.Search; using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests @@ -105,7 +106,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests .Returns(ReportRejectionType.None); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -138,7 +139,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests .Setup(s => s.IsSatisfiedBy(It.IsAny())).Returns(ReportRejectionType.None); //Act - var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, new SearchResult(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -163,7 +164,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithQualityNotNeeded(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -187,7 +188,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithNullSeries(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -209,7 +210,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithMisMatchedSeries(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -231,7 +232,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithMatchingSeries(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -253,7 +254,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithMatchingSeries(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -280,7 +281,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithSuccessfulDownload(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -314,7 +315,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests .Returns(true); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1); //Assert result.Should().HaveCount(parseResults.Count); diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs index 62eae990f..688b3deaf 100644 --- a/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs @@ -27,7 +27,12 @@ namespace NzbDrone.Core.Datastore.Migrations new Column("NzbUrl", DbType.String, ColumnProperty.NotNull), new Column("NzbInfoUrl", DbType.String, ColumnProperty.Null), new Column("Success", DbType.Boolean, ColumnProperty.NotNull), - new Column("SearchError", DbType.Int32, ColumnProperty.NotNull) + new Column("SearchError", DbType.Int32, ColumnProperty.NotNull), + new Column("Quality", DbType.Int32, ColumnProperty.NotNull), + new Column("Proper", DbType.Boolean, ColumnProperty.NotNull), + new Column("Age", DbType.Int32, ColumnProperty.NotNull), + new Column("Language", DbType.Int32, ColumnProperty.NotNull), + new Column("Size", DbType.Int64, ColumnProperty.NotNull), }); } } diff --git a/NzbDrone.Core/Fluent.cs b/NzbDrone.Core/Fluent.cs index f27ec5b76..a66ccbe20 100644 --- a/NzbDrone.Core/Fluent.cs +++ b/NzbDrone.Core/Fluent.cs @@ -87,5 +87,60 @@ namespace NzbDrone.Core return s.Substring(0, i); } + public static string AddSpacesToEnum(this Enum enumValue) + { + var text = enumValue.ToString(); + + if (string.IsNullOrWhiteSpace(text)) + return ""; + var newText = new StringBuilder(text.Length * 2); + newText.Append(text[0]); + for (int i = 1; i < text.Length; i++) + { + if (char.IsUpper(text[i]) && text[i - 1] != ' ') + newText.Append(' '); + newText.Append(text[i]); + } + return newText.ToString(); + } + + private const Decimal ONE_KILOBYTE = 1024M; + private const Decimal ONE_MEGABYTE = ONE_KILOBYTE * 1024M; + private const Decimal ONE_GIGABYTE = ONE_MEGABYTE * 1024M; + + public static string ToBestFileSize(this long bytes, int precision = 0) + { + if (bytes == 0) + return "0B"; + + decimal size = Convert.ToDecimal(bytes); + + string suffix; + + if (size > ONE_GIGABYTE) + { + size /= ONE_GIGABYTE; + suffix = "GB"; + } + + else if (size > ONE_MEGABYTE) + { + size /= ONE_MEGABYTE; + suffix = "MB"; + } + + else if (size > ONE_KILOBYTE) + { + size /= ONE_KILOBYTE; + suffix = "KB"; + } + + else + { + suffix = " B"; + } + + return String.Format("{0:N" + precision + "} {1}", size, suffix); + } } } diff --git a/NzbDrone.Core/Helpers/FileSizeFormatHelper.cs b/NzbDrone.Core/Helpers/FileSizeFormatHelper.cs deleted file mode 100644 index 515543fc8..000000000 --- a/NzbDrone.Core/Helpers/FileSizeFormatHelper.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; - -namespace NzbDrone.Core.Helpers -{ - public static class FileSizeFormatHelper - { - private const Decimal OneKiloByte = 1024M; - private const Decimal OneMegaByte = OneKiloByte * 1024M; - private const Decimal OneGigaByte = OneMegaByte * 1024M; - - public static string Format(long bytes, int precision = 0) - { - if (bytes == 0) - return "0B"; - - decimal size = Convert.ToDecimal(bytes); - - string suffix; - - if (size > OneGigaByte) - { - size /= OneGigaByte; - suffix = "GB"; - } - - else if (size > OneMegaByte) - { - size /= OneMegaByte; - suffix = "MB"; - } - - else if (size > OneKiloByte) - { - size /= OneKiloByte; - suffix = "KB"; - } - - else - { - suffix = " B"; - } - - return String.Format("{0:N" + precision + "}{1}", size, suffix); - } - } -} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 4a65fae54..bf01b1298 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -240,7 +240,6 @@ - diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs index fd51c6c1d..1537657e7 100644 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ b/NzbDrone.Core/Providers/SearchProvider.cs @@ -91,12 +91,13 @@ namespace NzbDrone.Core.Providers e => e.EpisodeNumbers = episodeNumbers.ToList() ); - searchResult.SearchResultItems = ProcessSearchResults(notification, reports, series, seasonNumber); + searchResult.SearchResultItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber); + _searchResultProvider.Add(searchResult); - return (searchResult.SearchResultItems.Select(s => s.Success).Count() == episodeNumbers.Count); + return (searchResult.Successes.Count == episodeNumbers.Count); } - public virtual List PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) + public virtual List PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) { var searchResult = new SearchResult { @@ -110,12 +111,12 @@ namespace NzbDrone.Core.Providers if (series == null) { Logger.Error("Unable to find an series {0} in database", seriesId); - return new List(); + return new List(); } //Return empty list if the series is a daily series (we only support individual episode searching if (series.IsDaily) - return new List(); + return new List(); notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber); var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber); @@ -123,13 +124,13 @@ namespace NzbDrone.Core.Providers Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); if (reports.Count == 0) - return new List(); + return new List(); notification.CurrentMessage = "Processing search results"; - searchResult.SearchResultItems = ProcessSearchResults(notification, reports, series, seasonNumber); + searchResult.SearchResultItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber); _searchResultProvider.Add(searchResult); - return searchResult.SearchResultItems; + return searchResult.Successes; } public virtual bool EpisodeSearch(ProgressNotification notification, int episodeId) @@ -182,7 +183,7 @@ namespace NzbDrone.Core.Providers else { searchResult.EpisodeId = episodeId; - searchResult.SearchResultItems = ProcessSearchResults(notification, reports, episode.Series, episode.SeasonNumber, episode.EpisodeNumber); + searchResult.SearchResultItems = ProcessSearchResults(notification, reports, searchResult, episode.Series, episode.SeasonNumber, episode.EpisodeNumber); _searchResultProvider.Add(searchResult); if (searchResult.SearchResultItems.Any(r => r.Success)) @@ -256,7 +257,7 @@ namespace NzbDrone.Core.Providers return reports; } - public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, Series series, int seasonNumber, int? episodeNumber = null) + public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, SearchResult searchResult, Series series, int seasonNumber, int? episodeNumber = null) { var successes = new List(); var items = new List(); @@ -271,7 +272,12 @@ namespace NzbDrone.Core.Providers { ReportTitle = episodeParseResult.OriginalString, NzbUrl = episodeParseResult.NzbUrl, - Indexer = episodeParseResult.Indexer + Indexer = episodeParseResult.Indexer, + Quality = episodeParseResult.Quality.QualityType, + Proper = episodeParseResult.Quality.Proper, + Size = episodeParseResult.Size, + Age = episodeParseResult.Age, + Language = episodeParseResult.Language }; items.Add(item); @@ -360,7 +366,12 @@ namespace NzbDrone.Core.Providers { ReportTitle = episodeParseResult.OriginalString, NzbUrl = episodeParseResult.NzbUrl, - Indexer = episodeParseResult.Indexer + Indexer = episodeParseResult.Indexer, + Quality = episodeParseResult.Quality.QualityType, + Proper = episodeParseResult.Quality.Proper, + Size = episodeParseResult.Size, + Age = episodeParseResult.Age, + Language = episodeParseResult.Language }; items.Add(item); @@ -414,6 +425,7 @@ namespace NzbDrone.Core.Providers { Logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e); notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult); + item.SearchError = ReportRejectionType.DownloadClientFailure; } } } diff --git a/NzbDrone.Core/Providers/SearchResultProvider.cs b/NzbDrone.Core/Providers/SearchResultProvider.cs index cba3dc697..f01b9ffdb 100644 --- a/NzbDrone.Core/Providers/SearchResultProvider.cs +++ b/NzbDrone.Core/Providers/SearchResultProvider.cs @@ -13,12 +13,17 @@ namespace NzbDrone.Core.Providers public class SearchResultProvider { private readonly IDatabase _database; + private readonly SeriesProvider _seriesProvider; + private readonly DownloadProvider _downloadProvider; private static readonly Logger logger = LogManager.GetCurrentClassLogger(); [Inject] - public SearchResultProvider(IDatabase database) + public SearchResultProvider(IDatabase database, SeriesProvider seriesProvider, + DownloadProvider downloadProvider) { _database = database; + _seriesProvider = seriesProvider; + _downloadProvider = downloadProvider; } public SearchResultProvider() @@ -54,7 +59,7 @@ namespace NzbDrone.Core.Providers Episodes.EpisodeNumber, Episodes.SeasonNumber, Episodes.Title as EpisodeTitle, Episodes.AirDate, Count(SearchResultItems.Id) as TotalItems, - SUM(CASE WHEN SearchResultItems.Success = 1 THEN 1 ELSE 0 END) as Successes + SUM(CASE WHEN SearchResultItems.Success = 1 THEN 1 ELSE 0 END) as SuccessfulCount FROM SearchResults INNER JOIN Series ON Series.SeriesId = SearchResults.SeriesId @@ -73,10 +78,36 @@ namespace NzbDrone.Core.Providers public virtual SearchResult GetSearchResult(int id) { - var result = _database.Single(id); + var sql = @"SELECT SearchResults.Id, SearchResults.SeriesId, SearchResults.SeasonNumber, + SearchResults.EpisodeId, SearchResults.SearchTime, + Series.Title as SeriesTitle, Series.IsDaily, + Episodes.EpisodeNumber, Episodes.SeasonNumber, Episodes.Title as EpisodeTitle, + Episodes.AirDate + FROM SearchResults + INNER JOIN Series + ON Series.SeriesId = SearchResults.SeriesId + LEFT JOIN Episodes + ON Episodes.EpisodeId = SearchResults.EpisodeId + WHERE SearchResults.Id = @0"; + + var result = _database.Single(sql, id); result.SearchResultItems = _database.Fetch("WHERE SearchResultId = @0", id); return result; } + + public virtual void ForceDownload(int itemId) + { + var item = _database.Single(itemId); + var searchResult = _database.Single(item.SearchResultId); + var series = _seriesProvider.GetSeries(searchResult.SeriesId); + + var parseResult = Parser.ParseTitle(item.ReportTitle); + parseResult.NzbUrl = item.NzbUrl; + parseResult.Series = series; + parseResult.Indexer = item.Indexer; + + _downloadProvider.DownloadReport(parseResult); + } } } diff --git a/NzbDrone.Core/Repository/Search/SearchResult.cs b/NzbDrone.Core/Repository/Search/SearchResult.cs index 50ef04550..1d5069940 100644 --- a/NzbDrone.Core/Repository/Search/SearchResult.cs +++ b/NzbDrone.Core/Repository/Search/SearchResult.cs @@ -21,6 +21,9 @@ namespace NzbDrone.Core.Repository.Search [ResultColumn] public List SearchResultItems { get; set; } + [Ignore] + public List Successes { get; set; } + [ResultColumn] public string SeriesTitle { get; set; } @@ -40,6 +43,6 @@ namespace NzbDrone.Core.Repository.Search public int TotalItems { get; set; } [ResultColumn] - public int Successes { get; set; } + public int SuccessfulCount { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Repository/Search/SearchResultItem.cs b/NzbDrone.Core/Repository/Search/SearchResultItem.cs index 51b744e59..091783319 100644 --- a/NzbDrone.Core/Repository/Search/SearchResultItem.cs +++ b/NzbDrone.Core/Repository/Search/SearchResultItem.cs @@ -19,5 +19,15 @@ namespace NzbDrone.Core.Repository.Search public string NzbInfoUrl { get; set; } public bool Success { get; set; } public ReportRejectionType SearchError { get; set; } + public QualityTypes Quality { get; set; } + public bool Proper { get; set; } + public int Age { get; set; } + public LanguageType Language { get; set; } + public long Size { get; set; } + + public override string ToString() + { + return String.Format("{0} - {1} - {2}", ReportTitle, Quality, SearchError); + } } } \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/LogController.cs b/NzbDrone.Web/Controllers/LogController.cs index 5db86794c..6df8bd2ad 100644 --- a/NzbDrone.Web/Controllers/LogController.cs +++ b/NzbDrone.Web/Controllers/LogController.cs @@ -4,11 +4,10 @@ using System.Linq; using System.Linq.Dynamic; using System.Text; using System.Web.Mvc; +using DataTables.Mvc.Core; using DataTables.Mvc.Core.Models; using NzbDrone.Common; using NzbDrone.Core.Instrumentation; -using NzbDrone.Core.Providers; -using NzbDrone.Core.Repository.Search; using NzbDrone.Web.Models; namespace NzbDrone.Web.Controllers @@ -18,15 +17,13 @@ namespace NzbDrone.Web.Controllers private readonly LogProvider _logProvider; private readonly EnvironmentProvider _environmentProvider; private readonly DiskProvider _diskProvider; - private readonly SearchResultProvider _searchResultProvider; public LogController(LogProvider logProvider, EnvironmentProvider environmentProvider, - DiskProvider diskProvider, SearchResultProvider searchResultProvider) + DiskProvider diskProvider) { _logProvider = logProvider; _environmentProvider = environmentProvider; _diskProvider = diskProvider; - _searchResultProvider = searchResultProvider; } public ActionResult Index() @@ -55,28 +52,6 @@ namespace NzbDrone.Web.Controllers return JsonNotificationResult.Info("Logs Cleared"); } - public ActionResult SearchResults() - { - var results = _searchResultProvider.AllSearchResults(); - - var model = results.Select(s => new SearchResultsModel - { - Id = s.Id, - SearchTime = s.SearchTime.ToString(), - DisplayName = GetDisplayName(s), - ReportCount = s.TotalItems, - Successful = s.Successes > 0 - }); - - return View(model); - } - - public ActionResult SearchDetails(int searchId) - { - var model = _searchResultProvider.GetSearchResult(searchId); - return View(model); - } - public ActionResult AjaxBinding(DataTablesParams dataTablesParams) { var logs = _logProvider.GetAllLogs(); @@ -129,24 +104,5 @@ namespace NzbDrone.Web.Controllers }, JsonRequestBehavior.AllowGet); } - - public string GetDisplayName(SearchResult searchResult) - { - if (!searchResult.EpisodeNumber.HasValue) - { - return String.Format("{0} - Season {1}", searchResult.SeriesTitle, searchResult.SeasonNumber); - } - - string episodeString; - - if (searchResult.IsDaily) - episodeString = searchResult.AirDate.ToShortDateString().Replace('/', '-'); - - else - episodeString = String.Format("S{0:00}E{1:00}", searchResult.SeasonNumber, - searchResult.EpisodeNumber); - - return String.Format("{0} - {1} - {2}", searchResult.SeriesTitle, episodeString, searchResult.EpisodeTitle); - } } } \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/SearchResultController.cs b/NzbDrone.Web/Controllers/SearchResultController.cs new file mode 100644 index 000000000..ea5867095 --- /dev/null +++ b/NzbDrone.Web/Controllers/SearchResultController.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using NzbDrone.Core; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository.Search; +using NzbDrone.Web.Models; + +namespace NzbDrone.Web.Controllers +{ + public class SearchResultController : Controller + { + private readonly SearchResultProvider _searchResultProvider; + + public SearchResultController(SearchResultProvider searchResultProvider) + { + _searchResultProvider = searchResultProvider; + } + + public ActionResult Index() + { + var results = _searchResultProvider.AllSearchResults(); + + var model = results.Select(s => new SearchResultsModel + { + Id = s.Id, + SearchTime = s.SearchTime.ToString(), + DisplayName = GetDisplayName(s), + ReportCount = s.TotalItems, + Successful = s.SuccessfulCount > 0 + }); + + return View(model); + } + + public ActionResult Details(int searchId) + { + var searchResult = _searchResultProvider.GetSearchResult(searchId); + var model = new SearchDetailsModel + { + Id = searchResult.Id, + DisplayName = GetDisplayName(searchResult), + SearchResultItems = + searchResult.SearchResultItems.Select(s => new SearchItemModel + { + Id = s.Id, + ReportTitle = s.ReportTitle, + Indexer = s.Indexer, + NzbUrl = s.NzbUrl, + NzbInfoUrl = s.NzbInfoUrl, + Success = s.Success, + SearchError = s.SearchError.AddSpacesToEnum().Replace("None", "Grabbed"), + Quality = s.Quality.ToString(), + QualityInt = (int)s.Quality, + Proper = s.Proper, + Age = s.Age, + Size = s.Size.ToBestFileSize(1), + Language = s.Language.ToString() + }).ToList() + }; + + return View(model); + } + + public JsonResult ForceDownload(int id) + { + _searchResultProvider.ForceDownload(id); + + return new JsonResult { Data = "ok", JsonRequestBehavior = JsonRequestBehavior.AllowGet }; + } + + public string GetDisplayName(SearchResult searchResult) + { + if (!searchResult.EpisodeNumber.HasValue) + { + return String.Format("{0} - Season {1}", searchResult.SeriesTitle, searchResult.SeasonNumber); + } + + string episodeString; + + if (searchResult.IsDaily) + episodeString = searchResult.AirDate.ToShortDateString().Replace('/', '-'); + + else + episodeString = String.Format("S{0:00}E{1:00}", searchResult.SeasonNumber, + searchResult.EpisodeNumber); + + return String.Format("{0} - {1} - {2}", searchResult.SeriesTitle, episodeString, searchResult.EpisodeTitle); + } + } +} diff --git a/NzbDrone.Web/Controllers/SystemController.cs b/NzbDrone.Web/Controllers/SystemController.cs index a8c7406da..da688d12e 100644 --- a/NzbDrone.Web/Controllers/SystemController.cs +++ b/NzbDrone.Web/Controllers/SystemController.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Web.Mvc; using System.Web.Script.Serialization; using NzbDrone.Common; +using NzbDrone.Core; using NzbDrone.Core.Helpers; using NzbDrone.Core.Jobs; using NzbDrone.Core.Providers; @@ -129,7 +130,7 @@ namespace NzbDrone.Web.Controllers foreach (var fileInfo in files) { fileResult += String.Format("
{0}
{1}
", fileInfo.Name, - FileSizeFormatHelper.Format(fileInfo.Length, 1)); + fileInfo.Length.ToBestFileSize(1)); } model.Files = fileResult; diff --git a/NzbDrone.Web/Models/SearchDetailsModel.cs b/NzbDrone.Web/Models/SearchDetailsModel.cs new file mode 100644 index 000000000..ce198cf59 --- /dev/null +++ b/NzbDrone.Web/Models/SearchDetailsModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using NzbDrone.Core.Repository.Search; + +namespace NzbDrone.Web.Models +{ + public class SearchDetailsModel + { + public int Id { get; set; } + public string DisplayName { get; set; } + public List SearchResultItems { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Web/Models/SearchItemModel.cs b/NzbDrone.Web/Models/SearchItemModel.cs new file mode 100644 index 000000000..7e9821a18 --- /dev/null +++ b/NzbDrone.Web/Models/SearchItemModel.cs @@ -0,0 +1,23 @@ +using NzbDrone.Core.Model; +using NzbDrone.Core.Repository.Quality; + +namespace NzbDrone.Web.Models +{ + public class SearchItemModel + { + public int Id { get; set; } + public string ReportTitle { get; set; } + public string Indexer { get; set; } + public string NzbUrl { get; set; } + public string NzbInfoUrl { get; set; } + public bool Success { get; set; } + public string SearchError { get; set; } + public string Quality { get; set; } + public int QualityInt { get; set; } + public bool Proper { get; set; } + public int Age { get; set; } + public string Language { get; set; } + public string Size { get; set; } + public string Details { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index 778b6edcf..16dab8a16 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -52,7 +52,8 @@
- ..\packages\DataTables.Mvc.0.1.0.54\lib\DataTables.Mvc.Core.dll + ..\packages\DataTables.Mvc.0.1.0.67\lib\DataTables.Mvc.Core.dll + True ..\packages\DynamicQuery.1.0\lib\35\Dynamic.dll @@ -141,6 +142,7 @@ + @@ -234,9 +236,11 @@ + + @@ -521,7 +525,10 @@ - + + + + 10.0 diff --git a/NzbDrone.Web/Views/SearchResult/Details.cshtml b/NzbDrone.Web/Views/SearchResult/Details.cshtml new file mode 100644 index 000000000..701b07804 --- /dev/null +++ b/NzbDrone.Web/Views/SearchResult/Details.cshtml @@ -0,0 +1,45 @@ +@using DataTables.Mvc.Core +@using DataTables.Mvc.Core.Enum +@model NzbDrone.Web.Models.SearchDetailsModel + +@{ + ViewBag.Title = "Search Details"; +} + +

@Model.DisplayName

+ +@Html.GridHtml("searchDetailsGrid") + +@section Scripts +{ + @(Html.GridScriptFor(m => m.SearchResultItems, "#searchDetailsGrid") + .PageLength(20) + .ChangePageLength(false) + .AddColumn(new Column().Image("/Content/Images/Indexers/{Indexer}.png", new { alt = "{Indexer}", title = "{Indexer}" }, "{Indexer}").Sortable(false).Title("").Width("20px")) + .AddColumn(new Column().DataProperty("ReportTitle").Title("Report Title")) + .AddColumn(new Column().DataProperty("Success").Title("Successful").Width("120px")) + .AddColumn(new Column().DisplayAndSort("Quality", "QualityInt").Title("Quality").Width("80px")) + .AddColumn(new Column().DataProperty("SearchError").Title("Error")) + .AddColumn(new Column().DataProperty("return actionColumn(source, type, val);", true)) + .AddColumn(new Column().DataProperty("Details").RenderFunction("return getDetails(row, val);").Visible(false)) + .AddSorting(3, SortDirection.Desc)) + + +} diff --git a/NzbDrone.Web/Views/Log/SearchResults.cshtml b/NzbDrone.Web/Views/SearchResult/Index.cshtml similarity index 84% rename from NzbDrone.Web/Views/Log/SearchResults.cshtml rename to NzbDrone.Web/Views/SearchResult/Index.cshtml index 4c1b9057c..d28fc1b6f 100644 --- a/NzbDrone.Web/Views/Log/SearchResults.cshtml +++ b/NzbDrone.Web/Views/SearchResult/Index.cshtml @@ -10,10 +10,10 @@ @section Scripts { @( - Html.GridScriptForModel("#searchResultsGrid") + Html.GridScriptForModel("#searchResultsGrid") .PageLength(20) .ChangePageLength(false) - .AddColumn(new Column().DataProperty("DisplayName").Link("SearchDetails?searchId={Id}", "{DisplayName}").Title("Name")) + .AddColumn(new Column().DataProperty("DisplayName").Link("SearchResult/Details?searchId={Id}", "{DisplayName}", null).Title("Name")) .AddColumn(new Column().DataProperty("SearchTime").Title("Time").Width("170px")) .AddColumn(new Column().DataProperty("ReportCount").Title("Reports Found").Width("140px")) .AddColumn(new Column().DataProperty("Successful").Title("Successful").Width("110px")) diff --git a/NzbDrone.Web/packages.config b/NzbDrone.Web/packages.config index 1427f9d68..c6a1961b2 100644 --- a/NzbDrone.Web/packages.config +++ b/NzbDrone.Web/packages.config @@ -1,6 +1,6 @@  - + diff --git a/packages/DataTables.Mvc.0.1.0.54/DataTables.Mvc.0.1.0.54.nupkg b/packages/DataTables.Mvc.0.1.0.54/DataTables.Mvc.0.1.0.54.nupkg deleted file mode 100644 index 28848589edddfda1b9838ce1fd179fc3a01303d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13236 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 diff --git a/packages/DataTables.Mvc.0.1.0.54/lib/DataTables.Mvc.Core.dll b/packages/DataTables.Mvc.0.1.0.54/lib/DataTables.Mvc.Core.dll deleted file mode 100644 index c6457bd9bb55d5c6ed73de9c6a816361cd8729fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18432 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 Of6Y$*-S)rL2>d@pd4Qn+ diff --git a/packages/DataTables.Mvc.0.1.0.67/Content/App_Start/DataTablesMvc.cs.pp b/packages/DataTables.Mvc.0.1.0.67/Content/App_Start/DataTablesMvc.cs.pp new file mode 100644 index 000000000..abaaa76f4 --- /dev/null +++ b/packages/DataTables.Mvc.0.1.0.67/Content/App_Start/DataTablesMvc.cs.pp @@ -0,0 +1,17 @@ +using DataTables.Mvc.Core.Helpers; +using DataTables.Mvc.Core.Models; +using System.Web.Mvc; + +[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.App_Start.DataTablesModelBinderActivator), "Start")] + +namespace $rootnamespace$.App_Start +{ + public static class DataTablesModelBinderActivator + { + public static void Start() + { + if (!ModelBinders.Binders.ContainsKey(typeof(DataTablesParams))) + ModelBinders.Binders.Add(typeof(DataTablesParams), new DataTablesModelBinder()); + } + } +} \ No newline at end of file diff --git a/packages/DataTables.Mvc.0.1.0.67/DataTables.Mvc.0.1.0.67.nupkg b/packages/DataTables.Mvc.0.1.0.67/DataTables.Mvc.0.1.0.67.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..a3bc6861ce1f1a854564ccd8d315eedebea715e7 GIT binary patch literal 13931 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 literal 0 HcmV?d00001 diff --git a/packages/DataTables.Mvc.0.1.0.67/tools/install.ps1 b/packages/DataTables.Mvc.0.1.0.67/tools/install.ps1 new file mode 100644 index 000000000..7e1f5357f --- /dev/null +++ b/packages/DataTables.Mvc.0.1.0.67/tools/install.ps1 @@ -0,0 +1,5 @@ +param($installPath, $toolsPath, $package, $project) + +$path = [System.IO.Path] +$appstart = $path::Combine($path::GetDirectoryName($project.FileName), "App_Start\DataTablesMvc.cs") +$DTE.ItemOperations.OpenFile($appstart) \ No newline at end of file From 06df8a86b60b51ddca761c0d415c96f8f1e88f37 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 22 Apr 2012 23:47:30 -0700 Subject: [PATCH 6/7] Forced downloads now have episode titles. --- NzbDrone.Core/Providers/DownloadProvider.cs | 2 +- NzbDrone.Core/Providers/SearchResultProvider.cs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/NzbDrone.Core/Providers/DownloadProvider.cs b/NzbDrone.Core/Providers/DownloadProvider.cs index f9473c0bc..521491f4f 100644 --- a/NzbDrone.Core/Providers/DownloadProvider.cs +++ b/NzbDrone.Core/Providers/DownloadProvider.cs @@ -124,7 +124,7 @@ namespace NzbDrone.Core.Providers foreach (var episode in parseResult.EpisodeNumbers) { - episodeString.Add(String.Format("{0}x{1}", parseResult.SeasonNumber, episode)); + episodeString.Add(String.Format("{0}x{1:00}", parseResult.SeasonNumber, episode)); } var epNumberString = String.Join("-", episodeString); diff --git a/NzbDrone.Core/Providers/SearchResultProvider.cs b/NzbDrone.Core/Providers/SearchResultProvider.cs index f01b9ffdb..edd48b266 100644 --- a/NzbDrone.Core/Providers/SearchResultProvider.cs +++ b/NzbDrone.Core/Providers/SearchResultProvider.cs @@ -15,15 +15,18 @@ namespace NzbDrone.Core.Providers private readonly IDatabase _database; private readonly SeriesProvider _seriesProvider; private readonly DownloadProvider _downloadProvider; + private readonly EpisodeProvider _episodeProvider; + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); [Inject] public SearchResultProvider(IDatabase database, SeriesProvider seriesProvider, - DownloadProvider downloadProvider) + DownloadProvider downloadProvider, EpisodeProvider episodeProvider) { _database = database; _seriesProvider = seriesProvider; _downloadProvider = downloadProvider; + _episodeProvider = episodeProvider; } public SearchResultProvider() @@ -101,11 +104,12 @@ namespace NzbDrone.Core.Providers var item = _database.Single(itemId); var searchResult = _database.Single(item.SearchResultId); var series = _seriesProvider.GetSeries(searchResult.SeriesId); - + var parseResult = Parser.ParseTitle(item.ReportTitle); parseResult.NzbUrl = item.NzbUrl; parseResult.Series = series; parseResult.Indexer = item.Indexer; + var episodes = _episodeProvider.GetEpisodesByParseResult(parseResult); _downloadProvider.DownloadReport(parseResult); } From 38927e3ca1bac793cca7262d34f8f1a74cde0102 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 23 Apr 2012 00:38:42 -0700 Subject: [PATCH 7/7] Signalr errors will not be shown in the UI. New: Search History, review recent searches and force specific reports to download (Under history) --- .../JobTests/SeasonSearchJobTest.cs | 2 +- .../ProcessSearchResultsFixture.cs | 18 ++--- .../Datastore/Migrations/Migration20120420.cs | 6 +- NzbDrone.Core/NzbDrone.Core.csproj | 6 +- ...ltProvider.cs => SearchHistoryProvider.cs} | 66 +++++++++---------- NzbDrone.Core/Providers/SearchProvider.cs | 44 ++++++------- .../{SearchResult.cs => SearchHistory.cs} | 6 +- ...archResultItem.cs => SearchHistoryItem.cs} | 6 +- ...ntroller.cs => SearchHistoryController.cs} | 22 +++---- NzbDrone.Web/Models/SearchDetailsModel.cs | 2 +- ...hResultsModel.cs => SearchHistoryModel.cs} | 2 +- NzbDrone.Web/NzbDrone.Web.csproj | 8 +-- NzbDrone.Web/Scripts/NzbDrone/Notification.js | 2 +- NzbDrone.Web/Views/History/Index.cshtml | 1 + .../Details.cshtml | 4 +- .../Index.cshtml | 4 +- 16 files changed, 100 insertions(+), 99 deletions(-) rename NzbDrone.Core/Providers/{SearchResultProvider.cs => SearchHistoryProvider.cs} (55%) rename NzbDrone.Core/Repository/Search/{SearchResult.cs => SearchHistory.cs} (89%) rename NzbDrone.Core/Repository/Search/{SearchResultItem.cs => SearchHistoryItem.cs} (88%) rename NzbDrone.Web/Controllers/{SearchResultController.cs => SearchHistoryController.cs} (77%) rename NzbDrone.Web/Models/{SearchResultsModel.cs => SearchHistoryModel.cs} (88%) rename NzbDrone.Web/Views/{SearchResult => SearchHistory}/Details.cshtml (86%) rename NzbDrone.Web/Views/{SearchResult => SearchHistory}/Index.cshtml (82%) diff --git a/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs index bb5eae2e1..8ca03f86b 100644 --- a/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs +++ b/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs @@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.JobTests [Test] public void SeasonSearch_partial_season_success() { - var resultItems = Builder.CreateListOfSize(5) + var resultItems = Builder.CreateListOfSize(5) .All() .With(e => e.SearchError = ReportRejectionType.None) .With(e => e.Success = true) diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs index 40c6022e4..145c8301a 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs @@ -106,7 +106,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests .Returns(ReportRejectionType.None); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -139,7 +139,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests .Setup(s => s.IsSatisfiedBy(It.IsAny())).Returns(ReportRejectionType.None); //Act - var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, new SearchResult(), _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, new SearchHistory(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -164,7 +164,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithQualityNotNeeded(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -188,7 +188,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithNullSeries(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -210,7 +210,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithMisMatchedSeries(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -232,7 +232,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithMatchingSeries(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -254,7 +254,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithMatchingSeries(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -281,7 +281,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests WithSuccessfulDownload(); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1); //Assert result.Should().HaveCount(parseResults.Count); @@ -315,7 +315,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests .Returns(true); //Act - var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchResult(), _matchingSeries, 1); + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1); //Assert result.Should().HaveCount(parseResults.Count); diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs index 688b3deaf..839d416f0 100644 --- a/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120420.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.Datastore.Migrations { protected override void MainDbUpgrade() { - Database.AddTable("SearchResults", new[] + Database.AddTable("SearchHistory", new[] { new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity), new Column("SeriesId", DbType.Int32, ColumnProperty.NotNull), @@ -18,10 +18,10 @@ namespace NzbDrone.Core.Datastore.Migrations new Column("SuccessfulDownload", DbType.Boolean, ColumnProperty.NotNull) }); - Database.AddTable("SearchResultItems", new[] + Database.AddTable("SearchHistoryItems", new[] { new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity), - new Column("SearchResultId", DbType.Int32, ColumnProperty.NotNull), + new Column("SearchHistoryId", DbType.Int32, ColumnProperty.NotNull), new Column("ReportTitle", DbType.String, ColumnProperty.NotNull), new Column("Indexer", DbType.String, ColumnProperty.NotNull), new Column("NzbUrl", DbType.String, ColumnProperty.NotNull), diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index bf01b1298..7d21a7066 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -277,7 +277,7 @@ - + @@ -305,8 +305,8 @@ Code - - + + diff --git a/NzbDrone.Core/Providers/SearchResultProvider.cs b/NzbDrone.Core/Providers/SearchHistoryProvider.cs similarity index 55% rename from NzbDrone.Core/Providers/SearchResultProvider.cs rename to NzbDrone.Core/Providers/SearchHistoryProvider.cs index edd48b266..3432cf273 100644 --- a/NzbDrone.Core/Providers/SearchResultProvider.cs +++ b/NzbDrone.Core/Providers/SearchHistoryProvider.cs @@ -10,7 +10,7 @@ using PetaPoco; namespace NzbDrone.Core.Providers { - public class SearchResultProvider + public class SearchHistoryProvider { private readonly IDatabase _database; private readonly SeriesProvider _seriesProvider; @@ -20,7 +20,7 @@ namespace NzbDrone.Core.Providers private static readonly Logger logger = LogManager.GetCurrentClassLogger(); [Inject] - public SearchResultProvider(IDatabase database, SeriesProvider seriesProvider, + public SearchHistoryProvider(IDatabase database, SeriesProvider seriesProvider, DownloadProvider downloadProvider, EpisodeProvider episodeProvider) { _database = database; @@ -29,80 +29,80 @@ namespace NzbDrone.Core.Providers _episodeProvider = episodeProvider; } - public SearchResultProvider() + public SearchHistoryProvider() { } - public virtual void Add(SearchResult searchResult) + public virtual void Add(SearchHistory searchResult) { logger.Trace("Adding new search result"); - searchResult.SuccessfulDownload = searchResult.SearchResultItems.Any(s => s.Success); + searchResult.SuccessfulDownload = searchResult.SearchHistoryItems.Any(s => s.Success); var id = Convert.ToInt32(_database.Insert(searchResult)); - searchResult.SearchResultItems.ForEach(s => s.SearchResultId = id); + searchResult.SearchHistoryItems.ForEach(s => s.SearchHistoryId = id); logger.Trace("Adding search result items"); - _database.InsertMany(searchResult.SearchResultItems); + _database.InsertMany(searchResult.SearchHistoryItems); } public virtual void Delete(int id) { logger.Trace("Deleting search result items attached to: {0}", id); - _database.Execute("DELETE FROM SearchResultItems WHERE SearchResultId = @0", id); + _database.Execute("DELETE FROM SearchHistoryItems WHERE SearchHistoryId = @0", id); logger.Trace("Deleting search result: {0}", id); - _database.Delete(id); + _database.Delete(id); } - public virtual List AllSearchResults() + public virtual List AllSearchHistory() { - var sql = @"SELECT SearchResults.Id, SearchResults.SeriesId, SearchResults.SeasonNumber, - SearchResults.EpisodeId, SearchResults.SearchTime, + var sql = @"SELECT SearchHistory.Id, SearchHistory.SeriesId, SearchHistory.SeasonNumber, + SearchHistory.EpisodeId, SearchHistory.SearchTime, Series.Title as SeriesTitle, Series.IsDaily, Episodes.EpisodeNumber, Episodes.SeasonNumber, Episodes.Title as EpisodeTitle, Episodes.AirDate, - Count(SearchResultItems.Id) as TotalItems, - SUM(CASE WHEN SearchResultItems.Success = 1 THEN 1 ELSE 0 END) as SuccessfulCount - FROM SearchResults + Count(SearchHistoryItems.Id) as TotalItems, + SUM(CASE WHEN SearchHistoryItems.Success = 1 THEN 1 ELSE 0 END) as SuccessfulCount + FROM SearchHistory INNER JOIN Series - ON Series.SeriesId = SearchResults.SeriesId + ON Series.SeriesId = SearchHistory.SeriesId LEFT JOIN Episodes - ON Episodes.EpisodeId = SearchResults.EpisodeId - INNER JOIN SearchResultItems - ON SearchResultItems.SearchResultId = SearchResults.Id - GROUP BY SearchResults.Id, SearchResults.SeriesId, SearchResults.SeasonNumber, - SearchResults.EpisodeId, SearchResults.SearchTime, + ON Episodes.EpisodeId = SearchHistory.EpisodeId + INNER JOIN SearchHistoryItems + ON SearchHistoryItems.SearchHistoryId = SearchHistory.Id + GROUP BY SearchHistory.Id, SearchHistory.SeriesId, SearchHistory.SeasonNumber, + SearchHistory.EpisodeId, SearchHistory.SearchTime, Series.Title, Series.IsDaily, Episodes.EpisodeNumber, Episodes.SeasonNumber, Episodes.Title, Episodes.AirDate"; - return _database.Fetch(sql); + return _database.Fetch(sql); } - public virtual SearchResult GetSearchResult(int id) + public virtual SearchHistory GetSearchHistory(int id) { - var sql = @"SELECT SearchResults.Id, SearchResults.SeriesId, SearchResults.SeasonNumber, - SearchResults.EpisodeId, SearchResults.SearchTime, + var sql = @"SELECT SearchHistory.Id, SearchHistory.SeriesId, SearchHistory.SeasonNumber, + SearchHistory.EpisodeId, SearchHistory.SearchTime, Series.Title as SeriesTitle, Series.IsDaily, Episodes.EpisodeNumber, Episodes.SeasonNumber, Episodes.Title as EpisodeTitle, Episodes.AirDate - FROM SearchResults + FROM SearchHistory INNER JOIN Series - ON Series.SeriesId = SearchResults.SeriesId + ON Series.SeriesId = SearchHistory.SeriesId LEFT JOIN Episodes - ON Episodes.EpisodeId = SearchResults.EpisodeId - WHERE SearchResults.Id = @0"; + ON Episodes.EpisodeId = SearchHistory.EpisodeId + WHERE SearchHistory.Id = @0"; - var result = _database.Single(sql, id); - result.SearchResultItems = _database.Fetch("WHERE SearchResultId = @0", id); + var result = _database.Single(sql, id); + result.SearchHistoryItems = _database.Fetch("WHERE SearchHistoryId = @0", id); return result; } public virtual void ForceDownload(int itemId) { - var item = _database.Single(itemId); - var searchResult = _database.Single(item.SearchResultId); + var item = _database.Single(itemId); + var searchResult = _database.Single(item.SearchHistoryId); var series = _seriesProvider.GetSeries(searchResult.SeriesId); var parseResult = Parser.ParseTitle(item.ReportTitle); diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs index 1537657e7..036411ecd 100644 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ b/NzbDrone.Core/Providers/SearchProvider.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.Providers private readonly SceneMappingProvider _sceneMappingProvider; private readonly UpgradePossibleSpecification _upgradePossibleSpecification; private readonly AllowedDownloadSpecification _allowedDownloadSpecification; - private readonly SearchResultProvider _searchResultProvider; + private readonly SearchHistoryProvider _searchHistoryProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Providers public SearchProvider(EpisodeProvider episodeProvider, DownloadProvider downloadProvider, SeriesProvider seriesProvider, IndexerProvider indexerProvider, SceneMappingProvider sceneMappingProvider, UpgradePossibleSpecification upgradePossibleSpecification, AllowedDownloadSpecification allowedDownloadSpecification, - SearchResultProvider searchResultProvider) + SearchHistoryProvider searchHistoryProvider) { _episodeProvider = episodeProvider; _downloadProvider = downloadProvider; @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Providers _sceneMappingProvider = sceneMappingProvider; _upgradePossibleSpecification = upgradePossibleSpecification; _allowedDownloadSpecification = allowedDownloadSpecification; - _searchResultProvider = searchResultProvider; + _searchHistoryProvider = searchHistoryProvider; } public SearchProvider() @@ -48,7 +48,7 @@ namespace NzbDrone.Core.Providers public virtual bool SeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) { - var searchResult = new SearchResult + var searchResult = new SearchHistory { SearchTime = DateTime.Now, SeriesId = seriesId, @@ -91,15 +91,15 @@ namespace NzbDrone.Core.Providers e => e.EpisodeNumbers = episodeNumbers.ToList() ); - searchResult.SearchResultItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber); - _searchResultProvider.Add(searchResult); + searchResult.SearchHistoryItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber); + _searchHistoryProvider.Add(searchResult); return (searchResult.Successes.Count == episodeNumbers.Count); } public virtual List PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) { - var searchResult = new SearchResult + var searchResult = new SearchHistory { SearchTime = DateTime.Now, SeriesId = seriesId, @@ -127,9 +127,9 @@ namespace NzbDrone.Core.Providers return new List(); notification.CurrentMessage = "Processing search results"; - searchResult.SearchResultItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber); + searchResult.SearchHistoryItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber); - _searchResultProvider.Add(searchResult); + _searchHistoryProvider.Add(searchResult); return searchResult.Successes; } @@ -160,7 +160,7 @@ namespace NzbDrone.Core.Providers return false; } - var searchResult = new SearchResult + var searchResult = new SearchHistory { SearchTime = DateTime.Now, SeriesId = episode.Series.SeriesId @@ -173,20 +173,20 @@ namespace NzbDrone.Core.Providers if (episode.Series.IsDaily) { - searchResult.SearchResultItems = ProcessSearchResults(notification, reports, episode.Series, episode.AirDate.Value); - _searchResultProvider.Add(searchResult); + searchResult.SearchHistoryItems = ProcessSearchResults(notification, reports, episode.Series, episode.AirDate.Value); + _searchHistoryProvider.Add(searchResult); - if (searchResult.SearchResultItems.Any(r => r.Success)) + if (searchResult.SearchHistoryItems.Any(r => r.Success)) return true; } else { searchResult.EpisodeId = episodeId; - searchResult.SearchResultItems = ProcessSearchResults(notification, reports, searchResult, episode.Series, episode.SeasonNumber, episode.EpisodeNumber); - _searchResultProvider.Add(searchResult); + searchResult.SearchHistoryItems = ProcessSearchResults(notification, reports, searchResult, episode.Series, episode.SeasonNumber, episode.EpisodeNumber); + _searchHistoryProvider.Add(searchResult); - if (searchResult.SearchResultItems.Any(r => r.Success)) + if (searchResult.SearchHistoryItems.Any(r => r.Success)) return true; } @@ -257,10 +257,10 @@ namespace NzbDrone.Core.Providers return reports; } - public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, SearchResult searchResult, Series series, int seasonNumber, int? episodeNumber = null) + public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, SearchHistory searchResult, Series series, int seasonNumber, int? episodeNumber = null) { var successes = new List(); - var items = new List(); + var items = new List(); foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality).ThenBy(c => c.Age)) { @@ -268,7 +268,7 @@ namespace NzbDrone.Core.Providers { Logger.Trace("Analysing report " + episodeParseResult); - var item = new SearchResultItem + var item = new SearchHistoryItem { ReportTitle = episodeParseResult.OriginalString, NzbUrl = episodeParseResult.NzbUrl, @@ -353,16 +353,16 @@ namespace NzbDrone.Core.Providers return items; } - public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, Series series, DateTime airDate) + public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, Series series, DateTime airDate) { - var items = new List(); + var items = new List(); var skip = false; foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality)) { try { - var item = new SearchResultItem + var item = new SearchHistoryItem { ReportTitle = episodeParseResult.OriginalString, NzbUrl = episodeParseResult.NzbUrl, diff --git a/NzbDrone.Core/Repository/Search/SearchResult.cs b/NzbDrone.Core/Repository/Search/SearchHistory.cs similarity index 89% rename from NzbDrone.Core/Repository/Search/SearchResult.cs rename to NzbDrone.Core/Repository/Search/SearchHistory.cs index 1d5069940..ce4c831aa 100644 --- a/NzbDrone.Core/Repository/Search/SearchResult.cs +++ b/NzbDrone.Core/Repository/Search/SearchHistory.cs @@ -8,8 +8,8 @@ using PetaPoco; namespace NzbDrone.Core.Repository.Search { [PrimaryKey("Id", autoIncrement = true)] - [TableName("SearchResults")] - public class SearchResult + [TableName("SearchHistory")] + public class SearchHistory { public int Id { get; set; } public int SeriesId { get; set; } @@ -19,7 +19,7 @@ namespace NzbDrone.Core.Repository.Search public bool SuccessfulDownload { get; set; } [ResultColumn] - public List SearchResultItems { get; set; } + public List SearchHistoryItems { get; set; } [Ignore] public List Successes { get; set; } diff --git a/NzbDrone.Core/Repository/Search/SearchResultItem.cs b/NzbDrone.Core/Repository/Search/SearchHistoryItem.cs similarity index 88% rename from NzbDrone.Core/Repository/Search/SearchResultItem.cs rename to NzbDrone.Core/Repository/Search/SearchHistoryItem.cs index 091783319..41ad06665 100644 --- a/NzbDrone.Core/Repository/Search/SearchResultItem.cs +++ b/NzbDrone.Core/Repository/Search/SearchHistoryItem.cs @@ -8,11 +8,11 @@ using PetaPoco; namespace NzbDrone.Core.Repository.Search { [PrimaryKey("Id", autoIncrement = true)] - [TableName("SearchResultItems")] - public class SearchResultItem + [TableName("SearchHistoryItems")] + public class SearchHistoryItem { public int Id { get; set; } - public int SearchResultId { get; set; } + public int SearchHistoryId { get; set; } public string ReportTitle { get; set; } public string Indexer { get; set; } public string NzbUrl { get; set; } diff --git a/NzbDrone.Web/Controllers/SearchResultController.cs b/NzbDrone.Web/Controllers/SearchHistoryController.cs similarity index 77% rename from NzbDrone.Web/Controllers/SearchResultController.cs rename to NzbDrone.Web/Controllers/SearchHistoryController.cs index ea5867095..96c696e33 100644 --- a/NzbDrone.Web/Controllers/SearchResultController.cs +++ b/NzbDrone.Web/Controllers/SearchHistoryController.cs @@ -10,20 +10,20 @@ using NzbDrone.Web.Models; namespace NzbDrone.Web.Controllers { - public class SearchResultController : Controller + public class SearchHistoryController : Controller { - private readonly SearchResultProvider _searchResultProvider; + private readonly SearchHistoryProvider _searchHistoryProvider; - public SearchResultController(SearchResultProvider searchResultProvider) + public SearchHistoryController(SearchHistoryProvider searchHistoryProvider) { - _searchResultProvider = searchResultProvider; + _searchHistoryProvider = searchHistoryProvider; } public ActionResult Index() { - var results = _searchResultProvider.AllSearchResults(); + var results = _searchHistoryProvider.AllSearchHistory(); - var model = results.Select(s => new SearchResultsModel + var model = results.Select(s => new SearchHistoryModel { Id = s.Id, SearchTime = s.SearchTime.ToString(), @@ -37,13 +37,13 @@ namespace NzbDrone.Web.Controllers public ActionResult Details(int searchId) { - var searchResult = _searchResultProvider.GetSearchResult(searchId); + var searchResult = _searchHistoryProvider.GetSearchHistory(searchId); var model = new SearchDetailsModel { Id = searchResult.Id, DisplayName = GetDisplayName(searchResult), - SearchResultItems = - searchResult.SearchResultItems.Select(s => new SearchItemModel + SearchHistoryItems = + searchResult.SearchHistoryItems.Select(s => new SearchItemModel { Id = s.Id, ReportTitle = s.ReportTitle, @@ -66,12 +66,12 @@ namespace NzbDrone.Web.Controllers public JsonResult ForceDownload(int id) { - _searchResultProvider.ForceDownload(id); + _searchHistoryProvider.ForceDownload(id); return new JsonResult { Data = "ok", JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } - public string GetDisplayName(SearchResult searchResult) + public string GetDisplayName(SearchHistory searchResult) { if (!searchResult.EpisodeNumber.HasValue) { diff --git a/NzbDrone.Web/Models/SearchDetailsModel.cs b/NzbDrone.Web/Models/SearchDetailsModel.cs index ce198cf59..e68a7762b 100644 --- a/NzbDrone.Web/Models/SearchDetailsModel.cs +++ b/NzbDrone.Web/Models/SearchDetailsModel.cs @@ -8,6 +8,6 @@ namespace NzbDrone.Web.Models { public int Id { get; set; } public string DisplayName { get; set; } - public List SearchResultItems { get; set; } + public List SearchHistoryItems { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/Models/SearchResultsModel.cs b/NzbDrone.Web/Models/SearchHistoryModel.cs similarity index 88% rename from NzbDrone.Web/Models/SearchResultsModel.cs rename to NzbDrone.Web/Models/SearchHistoryModel.cs index 178844c1f..d7cd78857 100644 --- a/NzbDrone.Web/Models/SearchResultsModel.cs +++ b/NzbDrone.Web/Models/SearchHistoryModel.cs @@ -2,7 +2,7 @@ namespace NzbDrone.Web.Models { - public class SearchResultsModel + public class SearchHistoryModel { public int Id { get; set; } public string DisplayName { get; set; } diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index 16dab8a16..2770e93d3 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -142,7 +142,7 @@ - + @@ -241,7 +241,7 @@ - + @@ -525,10 +525,10 @@ - + - + 10.0 diff --git a/NzbDrone.Web/Scripts/NzbDrone/Notification.js b/NzbDrone.Web/Scripts/NzbDrone/Notification.js index 0eaa53086..7cad8c155 100644 --- a/NzbDrone.Web/Scripts/NzbDrone/Notification.js +++ b/NzbDrone.Web/Scripts/NzbDrone/Notification.js @@ -34,7 +34,7 @@ jqXHR.error(function (xhr, textStatus, thrownError) { //ignore notification errors. - if (this.url.indexOf("/notification/Comet") === 0 || this.url.indexOf("/Health/Index") === 0) + if (this.url.indexOf("/notification/Comet") === 0 || this.url.indexOf("/Health/Index") === 0 || this.url.indexOf("/signalr") === 0) return; alert("Status: " + textStatus + ", Error: " + thrownError); diff --git a/NzbDrone.Web/Views/History/Index.cshtml b/NzbDrone.Web/Views/History/Index.cshtml index 2b7e46ae6..799a6a325 100644 --- a/NzbDrone.Web/Views/History/Index.cshtml +++ b/NzbDrone.Web/Views/History/Index.cshtml @@ -4,6 +4,7 @@ } diff --git a/NzbDrone.Web/Views/SearchResult/Details.cshtml b/NzbDrone.Web/Views/SearchHistory/Details.cshtml similarity index 86% rename from NzbDrone.Web/Views/SearchResult/Details.cshtml rename to NzbDrone.Web/Views/SearchHistory/Details.cshtml index 701b07804..0546caba1 100644 --- a/NzbDrone.Web/Views/SearchResult/Details.cshtml +++ b/NzbDrone.Web/Views/SearchHistory/Details.cshtml @@ -12,7 +12,7 @@ @section Scripts { - @(Html.GridScriptFor(m => m.SearchResultItems, "#searchDetailsGrid") + @(Html.GridScriptFor(m => m.SearchHistoryItems, "#searchDetailsGrid") .PageLength(20) .ChangePageLength(false) .AddColumn(new Column().Image("/Content/Images/Indexers/{Indexer}.png", new { alt = "{Indexer}", title = "{Indexer}" }, "{Indexer}").Sortable(false).Title("").Width("20px")) @@ -36,7 +36,7 @@ function actionColumn(source, type, val) { if (type === 'display' || type === 'filter') { - return 'Force'; + return 'Force'; } // 'sort' and 'type' both just use the raw data return ''; diff --git a/NzbDrone.Web/Views/SearchResult/Index.cshtml b/NzbDrone.Web/Views/SearchHistory/Index.cshtml similarity index 82% rename from NzbDrone.Web/Views/SearchResult/Index.cshtml rename to NzbDrone.Web/Views/SearchHistory/Index.cshtml index d28fc1b6f..e5b8556e3 100644 --- a/NzbDrone.Web/Views/SearchResult/Index.cshtml +++ b/NzbDrone.Web/Views/SearchHistory/Index.cshtml @@ -1,5 +1,5 @@ @using DataTables.Mvc.Core -@model IEnumerable +@model IEnumerable @{ ViewBag.Title = "Search Results"; @@ -13,7 +13,7 @@ Html.GridScriptForModel("#searchResultsGrid") .PageLength(20) .ChangePageLength(false) - .AddColumn(new Column().DataProperty("DisplayName").Link("SearchResult/Details?searchId={Id}", "{DisplayName}", null).Title("Name")) + .AddColumn(new Column().DataProperty("DisplayName").Link("SearchHistory/Details?searchId={Id}", "{DisplayName}", null).Title("Name")) .AddColumn(new Column().DataProperty("SearchTime").Title("Time").Width("170px")) .AddColumn(new Column().DataProperty("ReportCount").Title("Reports Found").Width("140px")) .AddColumn(new Column().DataProperty("Successful").Title("Successful").Width("110px"))