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)
+ )
+}