Merge branch 'master' into nzburl

This commit is contained in:
Mark McDowall 2012-05-02 08:31:24 -07:00
commit 0c3de6964f
278 changed files with 106641 additions and 7944 deletions

6
.nuget/NuGet.Config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
</configuration>

BIN
.nuget/NuGet.exe Normal file

Binary file not shown.

71
.nuget/NuGet.targets Normal file
View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Windows specific commands -->
<NuGetToolsPath Condition=" '$(OS)' == 'Windows_NT'">$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
<PackagesConfig Condition=" '$(OS)' == 'Windows_NT'">$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
<PackagesDir Condition=" '$(OS)' == 'Windows_NT'">$([System.IO.Path]::Combine($(SolutionDir), "packages"))</PackagesDir>
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath Condition=" '$(OS)' != 'Windows_NT'">$(SolutionDir).nuget</NuGetToolsPath>
<PackagesConfig Condition=" '$(OS)' != 'Windows_NT' ">packages.config</PackagesConfig>
<PackagesDir Condition=" '$(OS)' != 'Windows_NT'">$(SolutionDir)packages</PackagesDir>
<!-- NuGet command -->
<NuGetExePath>$(NuGetToolsPath)\nuget.exe</NuGetExePath>
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(NuGetExePath)</NuGetCommand>
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
<!-- Package sources used to restore packages. By default will used the registered sources under %APPDATA%\NuGet\NuGet.Config -->
<PackageSources>""</PackageSources>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition="$(RestorePackages) == ''">false</RestorePackages>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition="$(BuildPackage) == ''">false</BuildPackage>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source $(PackageSources) -o "$(PackagesDir)"</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="!Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
</Project>

View File

@ -12,6 +12,8 @@
<AssemblyName>NzbDrone.App.Test</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -91,6 +93,7 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -12,6 +12,8 @@
<AssemblyName>NzbDrone.Common.Test</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -88,6 +90,7 @@
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -1,37 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace NzbDrone.Common.Contract
{
public class ExceptionReport : ReportBase
{
[JsonProperty("t")]
public string Type { get; set; }
[JsonProperty("l")]
public string Logger { get; set; }
[JsonProperty("lm")]
public string LogMessage { get; set; }
[JsonProperty("s")]
public string String { get; set; }
[JsonProperty("xmessage")]
public string ExceptionMessage { get; set; }
[JsonProperty("stk")]
public string Stack { get; set; }
protected override Dictionary<string, string> GetString()
{
var dic = new Dictionary<string, string>
{
{"ExType", Type.NullSafe()},
{"Logger", Logger.NullSafe()},
{"Message", LogMessage.NullSafe()},
{"Str", String.NullSafe()}
};
return dic;
}
}
}

View File

@ -1,11 +0,0 @@
using System.Linq;
using Newtonsoft.Json;
namespace NzbDrone.Common.Contract
{
public class ExceptionReportResponse
{
[JsonProperty("h")]
public string ExceptionHash { get; set; }
}
}

View File

@ -1,26 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace NzbDrone.Common.Contract
{
public class ExistingExceptionReport : ReportBase
{
[JsonProperty("h")]
public string Hash { get; set; }
[JsonProperty("lm")]
public string LogMessage { get; set; }
protected override Dictionary<string, string> GetString()
{
var dic = new Dictionary<string, string>
{
{"Message", LogMessage.NullSafe()}
};
return dic;
}
}
}

View File

@ -12,6 +12,8 @@
<AssemblyName>NzbDrone.Common</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -36,9 +38,11 @@
<Reference Include="Exceptioneer.WindowsFormsClient">
<HintPath>..\Libraries\Exceptioneer.WindowsFormsClient.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.0.8.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.4.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
<Reference Include="Exceptron.Driver, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Exceptron.Driver.0.1.0.8\lib\net20\Exceptron.Driver.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.4.5.4\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Ninject">
<HintPath>..\packages\Ninject.2.2.1.4\lib\net40-Full\Ninject.dll</HintPath>
@ -54,13 +58,10 @@
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="Contract\ExceptionReportResponse.cs" />
<Compile Include="Contract\ExistingExceptionReport.cs" />
<Compile Include="StringExtention.cs" />
<Compile Include="HttpProvider.cs" />
<Compile Include="ConfigFileProvider.cs" />
<Compile Include="ConsoleProvider.cs" />
<Compile Include="Contract\ExceptionReport.cs" />
<Compile Include="Contract\ReportBase.cs" />
<Compile Include="Contract\ParseErrorReport.cs" />
<Compile Include="NlogTargets\RemoteTarget.cs" />
@ -95,6 +96,7 @@
</COMReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Exceptron.Driver;
using NLog;
using NzbDrone.Common.Contract;
@ -12,12 +13,13 @@ namespace NzbDrone.Common
private const string SERVICE_URL = "http://services.nzbdrone.com/reporting";
private const string PARSE_URL = SERVICE_URL + "/ParseError";
private const string EXCEPTION_URL = SERVICE_URL + "/ReportException";
public static RestProvider RestProvider { get; set; }
private static readonly HashSet<string> parserErrorCache = new HashSet<string>();
public static ExceptionClient ExceptronDriver { get; set; }
private static readonly HashSet<string> parserErrorCache = new HashSet<string>();
public static void ClearCache()
{
lock (parserErrorCache)
@ -30,12 +32,12 @@ namespace NzbDrone.Common
{
try
{
VerifyRestProvider();
VerifyDependencies();
lock (parserErrorCache)
{
if (parserErrorCache.Contains(title.ToLower())) return;
parserErrorCache.Add(title.ToLower());
}
@ -54,20 +56,20 @@ namespace NzbDrone.Common
}
}
public static void ReportException(LogEventInfo logEvent)
public static string ReportException(LogEventInfo logEvent)
{
try
{
VerifyRestProvider();
var report = new ExceptionReport();
report.LogMessage = logEvent.FormattedMessage;
report.Stack = logEvent.Exception.StackTrace;
report.ExceptionMessage = logEvent.Exception.Message;
report.Logger = logEvent.LoggerName;
report.Type = logEvent.Exception.GetType().FullName;
VerifyDependencies();
RestProvider.PostData(EXCEPTION_URL, report);
var exceptionData = new ExceptionData();
exceptionData.Exception = logEvent.Exception;
exceptionData.Location = logEvent.LoggerName;
exceptionData.Message = logEvent.FormattedMessage;
exceptionData.UserId = EnvironmentProvider.UGuid.ToString().Replace("-", string.Empty);
return ExceptronDriver.SubmitException(exceptionData);
}
catch (Exception e)
{
@ -75,17 +77,32 @@ namespace NzbDrone.Common
{
throw;
}
//this shouldn't log an exception since it will cause a recursive loop.
logger.Info("Unable to report exception. " + e);
if (logEvent.LoggerName != logger.Name)//prevents a recursive loop.
{
logger.WarnException("Unable to report exception. ", e);
}
}
return null;
}
private static void VerifyRestProvider()
public static void SetupExceptronDriver()
{
if(RestProvider == null)
ExceptronDriver = new ExceptionClient("CB230C312E5C4FF38B4FB9644B05E60E")
{
ApplicationVersion = new EnvironmentProvider().Version.ToString()
};
ExceptronDriver.ThrowsExceptions = !EnvironmentProvider.IsProduction;
ExceptronDriver.Enviroment = EnvironmentProvider.IsProduction ? "Prod" : "Dev";
}
private static void VerifyDependencies()
{
if (RestProvider == null)
{
if(EnvironmentProvider.IsProduction)
if (EnvironmentProvider.IsProduction)
{
logger.Warn("Rest provider wasn't provided. creating new one!");
RestProvider = new RestProvider(new EnvironmentProvider());
@ -95,6 +112,19 @@ namespace NzbDrone.Common
throw new InvalidOperationException("REST Provider wasn't configured correctly.");
}
}
if (ExceptronDriver == null)
{
if (EnvironmentProvider.IsProduction)
{
logger.Warn("Exceptron Driver wasn't provided. creating new one!");
SetupExceptronDriver();
}
else
{
throw new InvalidOperationException("Exceptron Driver wasn't configured correctly.");
}
}
}
}
}

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="4.0.8" />
<package id="Exceptron.Driver" version="0.1.0.8" />
<package id="Newtonsoft.Json" version="4.5.4" />
<package id="NLog" version="2.0.0.2000" />
</packages>

View File

@ -12,7 +12,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.7.0" newVersion="4.0.7.0" />
<bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@ -63,6 +63,11 @@ namespace NzbDrone.Core.Test.Framework
}
}
protected static void ThrowException()
{
throw new ApplicationException("This is a message for test exception");
}
[TearDown]
public void CoreTestTearDown()
{

View File

@ -1,9 +1,10 @@
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.ServiceModel.Syndication;
using System.Threading;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
@ -530,5 +531,37 @@ namespace NzbDrone.Core.Test
parseResults.Should().HaveCount(1);
parseResults[0].CleanTitle.Should().Be("britainsgottalent");
}
[TestCase("wombles.xml", "de-de")]
public void dateTime_should_parse_when_using_other_cultures(string fileName, string culture)
{
var currentCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
Mocker.GetMock<HttpProvider>()
.Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>()))
.Returns(File.OpenRead(".\\Files\\Rss\\" + fileName));
var fakeSettings = Builder<IndexerDefinition>.CreateNew().Build();
Mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
var mockIndexer = Mocker.Resolve<MockIndexer>();
var parseResults = mockIndexer.FetchRss();
foreach (var episodeParseResult in parseResults)
{
var Uri = new Uri(episodeParseResult.NzbUrl);
Uri.PathAndQuery.Should().NotContain("//");
}
parseResults.Should().NotBeEmpty();
parseResults.Should().OnlyContain(s => s.Indexer == mockIndexer.Name);
parseResults.Should().OnlyContain(s => !String.IsNullOrEmpty(s.OriginalString));
parseResults.Should().OnlyContain(s => s.Age >= 0);
Thread.CurrentThread.CurrentCulture = currentCulture;
}
}
}

View File

@ -15,7 +15,6 @@ using PetaPoco;
namespace NzbDrone.Core.Test.Integeration
{
[TestFixture(Category = "ServiceIngeneration")]
[Explicit]
public class ServiceIntegerationFixture : CoreTest
{
private KernelBase _kernel;
@ -58,14 +57,27 @@ namespace NzbDrone.Core.Test.Integeration
[Test]
public void should_be_able_to_submit_exceptions()
{
ReportingService.RestProvider = new RestProvider(new EnvironmentProvider());
ReportingService.SetupExceptronDriver();
try
{
ThrowException();
}
catch (Exception e)
{
var log = new LogEventInfo
{
LoggerName = "LoggerName.LoggerName.LoggerName.LoggerName",
Exception = e,
Message = "New message string. New message string.",
};
var hash = ReportingService.ReportException(log);
hash.Should().HaveLength(8);
}
var log = new LogEventInfo();
log.LoggerName = "LoggerName.LoggerName.LoggerName.LoggerName";
log.Exception = new ArgumentOutOfRangeException();
log.Message = "New message string. New message string. New message string. New message string. New message string. New message string.";
ReportingService.ReportException(log);
}

View File

@ -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<SearchHistoryItem>.CreateListOfSize(5)
.All()
.With(e => e.SearchError = ReportRejectionType.None)
.With(e => e.Success = true)
.Build();
var episodes = Builder<Episode>.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");
@ -88,7 +96,7 @@ namespace NzbDrone.Core.Test.JobTests
Mocker.GetMock<SearchProvider>()
.Setup(c => c.PartialSeasonSearch(notification, 1, 1))
.Returns(new List<int>{1});
.Returns(new List<int>());
//Act
Mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);

View File

@ -12,6 +12,8 @@
<AssemblyName>NzbDrone.Core.Test</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -58,9 +60,9 @@
<Reference Include="Moq, Version=4.0.10827.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.0.8.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.4.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.4.5.3\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Ninject, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<HintPath>..\packages\Ninject.2.2.1.4\lib\net40-Full\Ninject.dll</HintPath>
@ -112,6 +114,7 @@
<Compile Include="ProviderTests\AnalyticsProviderTests\AnalyticsProviderFixture.cs" />
<Compile Include="ProviderTests\ConfigProviderTests\ConfigCachingFixture.cs" />
<Compile Include="ProviderTests\BannerProviderTest.cs" />
<Compile Include="ProviderTests\SearchHistoryProviderTest.cs" />
<Compile Include="ProviderTests\PlexProviderTest.cs" />
<Compile Include="ProviderTests\SeasonProviderTest.cs" />
<Compile Include="ProviderTests\DecisionEngineTests\RetentionSpecificationFixture.cs" />
@ -308,6 +311,7 @@
xcopy /s /y "$(SolutionDir)packages\SqlServerCompact.4.0.8482.1\NativeBinaries\x86\*.*" "$(TargetDir)"
</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -67,6 +67,8 @@ namespace NzbDrone.Core.Test
[TestCase("castle.2009.416.hdtv-lol", "Castle 2009", 4, 16)]
[TestCase("hawaii.five-0.2010.217.hdtv-lol", "Hawaii Five-0 (2010)", 2, 17)]
[TestCase("Looney Tunes - S1936E18 - I Love to Singa", "Looney Tunes", 1936, 18)]
[TestCase("American_Dad!_-_7x6_-_The_Scarlett_Getter_[SDTV]", "American Dad!", 7, 6)]
[TestCase("Falling_Skies_-_1x1_-_Live_and_Learn_[HDTV]", "Falling Skies", 1, 1)]
public void ParseTitle_single(string postTitle, string title, int seasonNumber, int episodeNumber)
{
var result = Parser.ParseTitle(postTitle);
@ -159,6 +161,14 @@ namespace NzbDrone.Core.Test
[TestCase("Gossip Girl S05E11 PROPER HDTV XviD 2HD", QualityTypes.SDTV, true)]
[TestCase("The Jonathan Ross Show S02E08 HDTV x264 FTP", QualityTypes.SDTV, false)]
[TestCase("White.Van.Man.2011.S02E01.WS.PDTV.x264-TLA", QualityTypes.SDTV, false)]
[TestCase("White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA", QualityTypes.SDTV, true)]
[TestCase("WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", QualityTypes.DVD, true)]
[TestCase("Pawn Stars S04E87 REPACK 720p HDTV x264 aAF", QualityTypes.HDTV, true)]
[TestCase("The Real Housewives of Vancouver S01E04 DSR x264 2HD", QualityTypes.SDTV, false)]
[TestCase("Vanguard S01E04 Mexicos Death Train DSR x264 MiNDTHEGAP", QualityTypes.SDTV, false)]
[TestCase("Vanguard S01E04 Mexicos Death Train 720 WEB DL", QualityTypes.WEBDL, false)]
[TestCase("Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", QualityTypes.WEBDL, false)]
[TestCase("Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", QualityTypes.WEBDL, false)]
public void quality_parse(string postTitle, object quality, bool proper)
{
var result = Parser.ParseQuality(postTitle);
@ -194,6 +204,9 @@ namespace NzbDrone.Core.Test
[TestCase("2x04x05.720p.BluRay-FUTV", "", 2, new[] { 4, 5 })]
[TestCase("S02E04E05.720p.BluRay-FUTV", "", 2, new[] { 4, 5 })]
[TestCase("S02E03-04-05.720p.BluRay-FUTV", "", 2, new[] { 3,4,5 })]
[TestCase("Breakout.Kings.S02E09-E10.HDTV.x264-ASAP", "Breakout Kings", 2, new[] { 9, 10 })]
[TestCase("Breakout Kings - 2x9-2x10 - Served Cold [SDTV] ", "Breakout Kings", 2, new[] { 9, 10 })]
[TestCase("Breakout Kings - 2x09-2x10 - Served Cold [SDTV] ", "Breakout Kings", 2, new[] { 9, 10 })]
public void TitleParse_multi(string postTitle, string title, int season, int[] episodes)
{
var result = Parser.ParseTitle(postTitle);

View File

@ -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);
}
}
}

View File

@ -212,5 +212,104 @@ namespace NzbDrone.Core.Test.ProviderTests
//Assert
result.Should().BeFalse();
}
[Test]
public void CleanUpDropFolder_should_do_nothing_if_no_files_are_found()
{
//Setup
var folder = @"C:\Test\DropDir\The Office";
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[0]);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(It.IsAny<string>()), Times.Never());
}
[Test]
public void CleanUpDropFolder_should_do_nothing_if_no_conflicting_files_are_found()
{
//Setup
var folder = @"C:\Test\DropDir\The Office";
var filename = Path.Combine(folder, "NotAProblem.avi");
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = 12345)
.Build();
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(() => null);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<SeriesProvider>().Verify(v => v.GetSeries(It.IsAny<int>()), Times.Never());
}
[Test]
public void CleanUpDropFolder_should_move_file_if_a_conflict_is_found()
{
//Setup
var folder = @"C:\Test\DropDir\The Office";
var filename = Path.Combine(folder, "Problem.avi");
var seriesId = 12345;
var newFilename = "S01E01 - Title";
var newFilePath = @"C:\Test\TV\The Office\Season 01\S01E01 - Title.avi";
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = seriesId)
.Build();
var series = Builder<Series>.CreateNew()
.With(s => s.SeriesId = seriesId)
.With(s => s.Title = "The Office")
.Build();
var episode = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeriesId = seriesId)
.With(e => e.EpisodeFileId = episodeFile.EpisodeFileId)
.Build();
Mocker.GetMock<MediaFileProvider>().Setup(v => v.GetFileByPath(filename))
.Returns(() => null);
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(episodeFile);
Mocker.GetMock<SeriesProvider>().Setup(s => s.GetSeries(It.IsAny<int>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>().Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId))
.Returns(episode);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetNewFilename(It.IsAny<IList<Episode>>(), series.Title, QualityTypes.Unknown, false))
.Returns(newFilename);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.CalculateFilePath(It.IsAny<Series>(), It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(new FileInfo(newFilePath));
Mocker.GetMock<DiskProvider>().Setup(s => s.MoveFile(episodeFile.Path, newFilePath));
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<DiskProvider>().Verify(v => v.MoveFile(filename.NormalizePath(), newFilePath), Times.Once());
}
}
}

View File

@ -179,12 +179,12 @@ namespace NzbDrone.Core.Test.ProviderTests
}
[TestCase(1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, false, Result = "My Series Name - 1x2 - My Episode Title [DVD]")]
[TestCase(1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, true, Result = "My Series Name - 1x2 - My Episode Title [DVD] [Proper]")]
[TestCase(1, new[] { 2 }, "", QualityTypes.DVD, true, Result = "My Series Name - 1x2 - [DVD] [Proper]")]
[TestCase(1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV, false, Result = "My Series Name - 1x2-1x4 - My Episode Title [HDTV]")]
[TestCase(1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV, true, Result = "My Series Name - 1x2-1x4 - My Episode Title [HDTV] [Proper]")]
[TestCase(1, new[] { 2, 4 }, "", QualityTypes.HDTV, true, Result = "My Series Name - 1x2-1x4 - [HDTV] [Proper]")]
[TestCase(1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, false, Result = "My Series Name - 1x02 - My Episode Title [DVD]")]
[TestCase(1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, true, Result = "My Series Name - 1x02 - My Episode Title [DVD] [Proper]")]
[TestCase(1, new[] { 2 }, "", QualityTypes.DVD, true, Result = "My Series Name - 1x02 - [DVD] [Proper]")]
[TestCase(1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV, false, Result = "My Series Name - 1x02-1x04 - My Episode Title [HDTV]")]
[TestCase(1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV, true, Result = "My Series Name - 1x02-1x04 - My Episode Title [HDTV] [Proper]")]
[TestCase(1, new[] { 2, 4 }, "", QualityTypes.HDTV, true, Result = "My Series Name - 1x02-1x04 - [HDTV] [Proper]")]
public string create_proper_sab_titles(int seasons, int[] episodes, string title, QualityTypes quality, bool proper)
{
var series = Builder<Series>.CreateNew()

View File

@ -158,5 +158,40 @@ namespace NzbDrone.Core.Test.ProviderTests
result.Should().HaveCount(9);
result.Should().NotContain(e => e.EpisodeFileId == 1);
}
[Test]
public void GetFileByPath_should_return_null_if_file_does_not_exist_in_database()
{
//Setup
WithRealDb();
//Act
var result = Mocker.Resolve<MediaFileProvider>().GetFileByPath(@"C:\Test\EpisodeFile.avi");
//Resolve
result.Should().BeNull();
}
[Test]
public void GetFileByPath_should_return_EpisodeFile_if_file_exists_in_database()
{
var path = @"C:\Test\EpisodeFile.avi";
//Setup
WithRealDb();
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = path.NormalizePath())
.Build();
var episodeFileId = Convert.ToInt32(Db.Insert(episodeFile));
//Act
var result = Mocker.Resolve<MediaFileProvider>().GetFileByPath(path);
//Resolve
result.Should().NotBeNull();
result.Path.Should().Be(path.NormalizePath());
result.EpisodeFileId.Should().Be(episodeFileId);
}
}
}

View File

@ -279,6 +279,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
.Build().ToList();
Mocker.GetMock<SeriesProvider>().Setup(s => s.FindSeries("office")).Returns(fakeSeries);
Mocker.GetMock<DiskScanProvider>().Setup(s => s.CleanUpDropFolder(droppedFolder.FullName));
Mocker.GetMock<DiskScanProvider>().Setup(s => s.Scan(fakeSeries, droppedFolder.FullName)).Returns(fakeEpisodeFiles);
Mocker.GetMock<DiskScanProvider>().Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), true)).Returns(true);
Mocker.GetMock<DiskProvider>().Setup(s => s.GetDirectorySize(droppedFolder.FullName)).Returns(Constants.IgnoreFileSize - 1.Megabytes());

View File

@ -0,0 +1,267 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Repository.Search;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.ProviderTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class SearchHistoryProviderTest : CoreTest
{
private SearchHistory _searchHistory;
private Series _series;
private Episode _episode;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew()
.Build();
_episode = Builder<Episode>.CreateNew()
.Build();
var items = Builder<SearchHistoryItem>.CreateListOfSize(10)
.Build().ToList();
_searchHistory = Builder<SearchHistory>.CreateNew()
.With(h => h.EpisodeId = _episode.EpisodeId)
.With(h => h.SeriesId - _series.SeriesId)
.With(h => h.SearchHistoryItems = items)
.Build();
}
private void WithUnsuccessfulSearch()
{
foreach(var item in _searchHistory.SearchHistoryItems)
{
item.Success = false;
}
}
private void WithSuccessfulSearch()
{
foreach(var item in _searchHistory.SearchHistoryItems)
{
item.Success = false;
}
var i = _searchHistory.SearchHistoryItems.Last();
i.Success = true;
i.SearchError = ReportRejectionType.None;
}
[Test]
public void Add_should_add_history_and_history_items()
{
WithRealDb();
Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
Db.Fetch<SearchHistory>().Should().HaveCount(1);
Db.Fetch<SearchHistoryItem>().Should().HaveCount(10);
}
[Test]
public void Add_should_add_return_id()
{
WithRealDb();
var result = Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
result.Should().NotBe(0);
}
[Test]
public void Delete_should_delete_history_and_history_items()
{
WithRealDb();
Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var history = Db.Fetch<SearchHistory>();
Mocker.Resolve<SearchHistoryProvider>().Delete(history.First().Id);
Db.Fetch<SearchHistory>().Should().HaveCount(0);
Db.Fetch<SearchHistoryItem>().Should().HaveCount(0);
}
[Test]
public void AllSearchHistory_should_return_all_items()
{
WithRealDb();
Db.Insert(_series);
Db.Insert(_episode);
Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var result = Mocker.Resolve<SearchHistoryProvider>().AllSearchHistory();
result.Count.Should().Be(1);
}
[Test]
public void AllSearchHistory_should_have_series_title()
{
WithRealDb();
Db.Insert(_series);
Db.Insert(_episode);
Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var result = Mocker.Resolve<SearchHistoryProvider>().AllSearchHistory();
result.Count.Should().Be(1);
result.First().SeriesTitle.Should().Be(_series.Title);
}
[Test]
public void AllSearchHistory_should_have_episode_information()
{
WithRealDb();
Db.Insert(_series);
Db.Insert(_episode);
Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var result = Mocker.Resolve<SearchHistoryProvider>().AllSearchHistory();
result.Count.Should().Be(1);
result.First().EpisodeTitle.Should().Be(_episode.Title);
result.First().EpisodeNumber.Should().Be(_episode.EpisodeNumber);
result.First().SeasonNumber.Should().Be(_episode.SeasonNumber);
}
[Test]
public void AllSearchHistory_should_have_totalItems_count()
{
WithRealDb();
Db.Insert(_series);
Db.Insert(_episode);
Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var result = Mocker.Resolve<SearchHistoryProvider>().AllSearchHistory();
result.Count.Should().Be(1);
result.First().TotalItems.Should().Be(_searchHistory.SearchHistoryItems.Count);
}
[Test]
public void AllSearchHistory_should_have_successfulCount_equal_zero_when_all_failed()
{
WithRealDb();
Db.Insert(_series);
Db.Insert(_episode);
WithUnsuccessfulSearch();
Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var result = Mocker.Resolve<SearchHistoryProvider>().AllSearchHistory();
result.Count.Should().Be(1);
result.First().SuccessfulCount.Should().Be(0);
}
[Test]
public void AllSearchHistory_should_have_successfulCount_equal_one_when_one_was_downloaded()
{
WithRealDb();
Db.Insert(_series);
Db.Insert(_episode);
WithSuccessfulSearch();
Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var result = Mocker.Resolve<SearchHistoryProvider>().AllSearchHistory();
result.Count.Should().Be(1);
result.First().SuccessfulCount.Should().Be(1);
}
[Test]
public void GetSearchHistory_should_return_searchHistory_with_items()
{
WithRealDb();
Db.Insert(_series);
Db.Insert(_episode);
WithSuccessfulSearch();
var id = Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var result = Mocker.Resolve<SearchHistoryProvider>().GetSearchHistory(id);
result.SearchHistoryItems.Should().HaveCount(_searchHistory.SearchHistoryItems.Count);
}
[Test]
public void GetSearchHistory_should_have_episodeDetails()
{
WithRealDb();
Db.Insert(_series);
Db.Insert(_episode);
WithSuccessfulSearch();
var id = Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var result = Mocker.Resolve<SearchHistoryProvider>().GetSearchHistory(id);
result.EpisodeNumber.Should().Be(_episode.EpisodeNumber);
result.SeasonNumber.Should().Be(_episode.SeasonNumber);
result.EpisodeTitle.Should().Be(_episode.Title);
result.AirDate.Should().Be(_episode.AirDate.Value);
}
[Test]
public void GetSearchHistory_should_not_have_episode_info_if_it_was_a_full_season_search()
{
WithRealDb();
Db.Insert(_series);
_searchHistory.EpisodeId = 0;
var id = Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var result = Mocker.Resolve<SearchHistoryProvider>().GetSearchHistory(id);
result.EpisodeNumber.Should().Be(null);
result.SeasonNumber.Should().Be(_searchHistory.SeasonNumber);
result.EpisodeTitle.Should().Be(null);
}
[Test]
public void ForceDownload_should_download_report()
{
WithRealDb();
Db.Insert(_series);
Db.Insert(_episode);
var reportTitle = String.Format("{0} - S{1:00}E{2:00}", _series.Title, _episode.SeasonNumber, _episode.EpisodeNumber);
_searchHistory.SearchHistoryItems.First().ReportTitle = reportTitle;
Mocker.Resolve<SearchHistoryProvider>().Add(_searchHistory);
var items = Db.Fetch<SearchHistoryItem>();
Mocker.Resolve<SearchHistoryProvider>().ForceDownload(items.First().Id);
Mocker.GetMock<DownloadProvider>().Verify(v => v.DownloadReport(It.IsAny<EpisodeParseResult>()), Times.Once());
}
}
}

View File

@ -77,14 +77,14 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
{
Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true);
.Returns(ReportRejectionType.None);
}
private void WithQualityNotNeeded()
{
Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false);
.Returns(ReportRejectionType.ExistingQualityIsEqualOrBetter);
}
[Test]
@ -103,13 +103,13 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p)))
.Returns(true);
.Returns(ReportRejectionType.None);
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert
result.Should().BeTrue();
result.Should().Contain(n => n.Success);
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Once());
@ -133,7 +133,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert
result.Should().BeFalse();
result.Should().NotContain(n => n.Success);
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Exactly(5));
@ -147,6 +147,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
.All()
.With(e => e.AirDate = DateTime.Today)
.With(e => e.Quality = new Quality(QualityTypes.HDTV, false))
.Build();
WithNullSeries();
@ -155,7 +156,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert
result.Should().BeFalse();
result.Should().NotContain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never());
@ -167,6 +168,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
.All()
.With(e => e.AirDate = DateTime.Today)
.With(e => e.Quality = new Quality(QualityTypes.HDTV, false))
.Build();
WithMisMatchedSeries();
@ -175,7 +177,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert
result.Should().BeFalse();
result.Should().NotContain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never());
@ -198,7 +200,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert
result.Should().BeTrue();
result.Should().Contain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Once());
@ -230,7 +232,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert
result.Should().BeTrue();
result.Should().Contain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Exactly(2));
@ -242,6 +244,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
.All()
.With(e => e.AirDate = null)
.With(e => e.Quality = new Quality(QualityTypes.HDTV, false))
.Build();
WithMatchingSeries();
@ -250,7 +253,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert
result.Should().BeFalse();
result.Should().NotContain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never());
@ -262,6 +265,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
.All()
.With(e => e.AirDate = DateTime.Today.AddDays(10))
.With(e => e.Quality = new Quality(QualityTypes.HDTV, false))
.Build();
WithMatchingSeries();
@ -270,7 +274,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert
result.Should().BeFalse();
result.Should().NotContain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never());

View File

@ -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
@ -73,14 +74,14 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
{
Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true);
.Returns(ReportRejectionType.None);
}
private void WithQualityNotNeeded()
{
Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false);
.Returns(ReportRejectionType.ExistingQualityIsEqualOrBetter);
}
[Test]
@ -102,14 +103,14 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p)))
.Returns(true);
.Returns(ReportRejectionType.None);
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _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<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Once());
@ -135,13 +136,14 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithSuccessfulDownload();
Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(true);
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(ReportRejectionType.None);
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, new SearchHistory(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(1);
result.Should().HaveCount(parseResults.Count);
result.Should().Contain(s => s.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.Is<EpisodeParseResult>(d => d.Age != 100)), Times.Never());
@ -162,10 +164,11 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithQualityNotNeeded();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(0);
result.Should().HaveCount(parseResults.Count);
result.Should().NotContain(s => s.Success);
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Exactly(5));
@ -180,15 +183,17 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
.All()
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeNumbers = new List<int> { 1 })
.With(e => e.Quality = new Quality(QualityTypes.HDTV, false))
.Build();
WithNullSeries();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(0);
result.Should().HaveCount(parseResults.Count);
result.Should().NotContain(s => s.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never());
@ -201,15 +206,17 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
.All()
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeNumbers = new List<int> { 1 })
.With(e => e.Quality = new Quality(QualityTypes.HDTV, false))
.Build();
WithMisMatchedSeries();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(0);
result.Should().HaveCount(parseResults.Count);
result.Should().NotContain(s => s.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never());
@ -222,15 +229,17 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
.All()
.With(e => e.SeasonNumber = 2)
.With(e => e.EpisodeNumbers = new List<int> { 1 })
.With(e => e.Quality = new Quality(QualityTypes.HDTV, false))
.Build();
WithMatchingSeries();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(0);
result.Should().HaveCount(parseResults.Count);
result.Should().NotContain(s => s.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never());
@ -243,15 +252,17 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
.All()
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeNumbers = new List<int> { 2 })
.With(e => e.Quality = new Quality(QualityTypes.HDTV, false))
.Build();
WithMatchingSeries();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1, 1);
//Assert
result.Should().HaveCount(0);
result.Should().HaveCount(parseResults.Count);
result.Should().NotContain(s => s.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never());
@ -274,10 +285,11 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithSuccessfulDownload();
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1);
//Assert
result.Should().HaveCount(1);
result.Should().HaveCount(parseResults.Count);
result.Should().Contain(s => s.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Once());
@ -307,10 +319,11 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
.Returns(true);
//Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1);
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, new SearchHistory(), _matchingSeries, 1);
//Assert
result.Should().HaveCount(1);
result.Should().HaveCount(parseResults.Count);
result.Should().Contain(s => s.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Exactly(2));

View File

@ -7,7 +7,7 @@
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" />
<package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1.1" />
<package id="Newtonsoft.Json" version="4.0.8" />
<package id="Newtonsoft.Json" version="4.5.3" />
<package id="Ninject" version="2.2.1.4" />
<package id="NLog" version="2.0.0.2000" />
<package id="NUnit" version="2.6.0.12054" />

View File

@ -68,6 +68,7 @@ namespace NzbDrone.Core
{
EnvironmentProvider.UGuid = Kernel.Get<ConfigProvider>().UGuid;
ReportingService.RestProvider = Kernel.Get<RestProvider>();
ReportingService.SetupExceptronDriver();
var appId = AnalyticsProvider.DESKMETRICS_TEST_ID;

View File

@ -0,0 +1,39 @@
using System.Data;
using Migrator.Framework;
namespace NzbDrone.Core.Datastore.Migrations
{
[Migration(20120420)]
public class Migration20120420 : NzbDroneMigration
{
protected override void MainDbUpgrade()
{
Database.AddTable("SearchHistory", 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("SearchHistoryItems", new[]
{
new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity),
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),
new Column("NzbInfoUrl", DbType.String, ColumnProperty.Null),
new Column("Success", DbType.Boolean, 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),
});
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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)

View File

@ -0,0 +1,21 @@
using System.Linq;
namespace NzbDrone.Core.Model
{
public enum ReportRejectionType
{
None = 0,
WrongSeries = 1,
QualityNotWanted = 2,
WrongSeason = 3,
WrongEpisode = 4,
Size = 5,
Retention = 6,
ExistingQualityIsEqualOrBetter = 7,
Cutoff = 8,
AlreadyInQueue = 9,
DownloadClientFailure = 10,
Skipped = 11,
Failure = 12,
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model
{
public class StatsModel
{
[DisplayName("Number of Series")]
public int SeriesTotal { get; set; }
[DisplayName("Number of Series Countinuing")]
public int SeriesContinuing { get; set; }
[DisplayName("Number of Series Ended")]
public int SeriesEnded { get; set; }
[DisplayName("Number of Episodes")]
public int EpisodesTotal { get; set; }
[DisplayName("Number of Episodes On Disk")]
public int EpisodesOnDisk { get; set; }
[DisplayName("Number of Episodes Missing")]
public int EpisodesMissing { get; set; }
[DisplayName("Downloaded in the Last Week")]
public int DownloadLastWeek { get; set; }
[DisplayName("Downloaded in the Last 30 days")]
public int DownloadedLastMonth { get; set; }
}
}

View File

@ -29,6 +29,8 @@
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
@ -158,9 +160,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\MiniProfiler.1.9\lib\net40\MvcMiniProfiler.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.0.8.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.4.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.4.5.3\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Ninject, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<HintPath>..\packages\Ninject.2.2.1.4\lib\net40-Full\Ninject.dll</HintPath>
@ -222,6 +224,7 @@
<Compile Include="Datastore\MigrationLogger.cs" />
<Compile Include="Datastore\MigrationsHelper.cs" />
<Compile Include="Datastore\CustomeMapper.cs" />
<Compile Include="Datastore\Migrations\Migration20120420.cs" />
<Compile Include="Datastore\Migrations\Migration20120228.cs" />
<Compile Include="Datastore\Migrations\Migration20120227.cs" />
<Compile Include="Datastore\Migrations\Migration20120220.cs" />
@ -239,7 +242,6 @@
<Compile Include="Datastore\PetaPoco\EpisodeSeasonRelator.cs" />
<Compile Include="Fluent.cs" />
<Compile Include="Helpers\EpisodeSortingHelper.cs" />
<Compile Include="Helpers\FileSizeFormatHelper.cs" />
<Compile Include="Helpers\SortHelper.cs" />
<Compile Include="Helpers\SabnzbdQueueTimeConverter.cs" />
<Compile Include="Jobs\CheckpointJob.cs" />
@ -264,6 +266,7 @@
<Compile Include="Model\Sabnzbd\SabModel.cs" />
<Compile Include="Model\Sabnzbd\SabQueueItem.cs" />
<Compile Include="Model\Sabnzbd\SabVersionModel.cs" />
<Compile Include="Model\StatsModel.cs" />
<Compile Include="Model\Twitter\TwitterAuthorizationModel.cs" />
<Compile Include="Model\UpdatePackage.cs" />
<Compile Include="Model\Xbmc\ActionType.cs" />
@ -277,6 +280,7 @@
<Compile Include="Providers\Indexer\NzbIndex.cs" />
<Compile Include="Providers\Indexer\FileSharingTalk.cs" />
<Compile Include="Providers\Indexer\Wombles.cs" />
<Compile Include="Providers\SearchHistoryProvider.cs" />
<Compile Include="Providers\SeasonProvider.cs" />
<Compile Include="Jobs\RecentBacklogSearchJob.cs" />
<Compile Include="Jobs\TrimLogsJob.cs" />
@ -304,6 +308,10 @@
<Compile Include="Providers\AnalyticsProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Providers\StatsProvider.cs" />
<Compile Include="Repository\Search\SearchHistoryItem.cs" />
<Compile Include="Repository\Search\SearchHistory.cs" />
<Compile Include="Model\ReportRejectionType.cs" />
<Compile Include="Repository\Season.cs" />
<Compile Include="Providers\AutoConfigureProvider.cs">
<SubType>Code</SubType>
@ -574,6 +582,7 @@
if not exist "$(TargetDir)x86" md "$(TargetDir)x86"
xcopy /s /y "$(SolutionDir)packages\SqlServerCompact.4.0.8482.1\NativeBinaries\x86\*.*" "$(TargetDir)x86"</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -26,7 +26,7 @@ namespace NzbDrone.Core
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Multi-episode Repeated (S01E05 - S01E06, 1x05 - 1x06, etc)
new Regex(@"^(?<title>.+?)(?:\W+S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:[ex]){1,2}(?<episode>\d{2}(?!\d+)))+){2,}\W?(?!\\)",
new Regex(@"^(?<title>.+?)(?:\W+S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:[ex]){1,2}(?<episode>\d{1,2}(?!\d+)))+){2,}\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Episodes without a title, Single (S01E05, 1x05) AND Multi (S01E04E05, 1x04x05, etc)
@ -65,7 +65,7 @@ namespace NzbDrone.Core
RegexOptions.IgnoreCase | RegexOptions.Compiled)
};
private static readonly Regex NormalizeRegex = new Regex(@"((^|\W)(a|an|the|and|or|of)($|\W))|\W|(?:(?<=[^0-9]+)|\b)(?!(?:19\d{2}|20\d{2}))\d+(?=[^0-9ip]+|\b)",
private static readonly Regex NormalizeRegex = new Regex(@"((^|\W)(a|an|the|and|or|of)($|\W))|\W|_|(?:(?<=[^0-9]+)|\b)(?!(?:19\d{2}|20\d{2}))\d+(?=[^0-9ip]+|\b)",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex SimpleTitleRegex = new Regex(@"480[i|p]|720[i|p]|1080[i|p]|[x|h|x\s|h\s]264|DD\W?5\W1|\<|\>|\?|\*|\:|\||""",
@ -251,7 +251,7 @@ namespace NzbDrone.Core
name = name.Trim();
var normalizedName = NormalizeTitle(name);
var result = new Quality { QualityType = QualityTypes.Unknown };
result.Proper = normalizedName.Contains("proper");
result.Proper = (normalizedName.Contains("proper") || normalizedName.Contains("repack"));
if (normalizedName.Contains("dvd") || normalizedName.Contains("bdrip") || normalizedName.Contains("brrip"))
{
@ -259,7 +259,7 @@ namespace NzbDrone.Core
return result;
}
if (normalizedName.Contains("xvid") || normalizedName.Contains("divx"))
if (normalizedName.Contains("xvid") || normalizedName.Contains("divx") || normalizedName.Contains("dsr"))
{
if (normalizedName.Contains("bluray"))
{
@ -300,8 +300,6 @@ namespace NzbDrone.Core
}
//Based on extension
if (result.QualityType == QualityTypes.Unknown)
{
try

View File

@ -25,6 +25,7 @@ namespace NzbDrone.Core.Providers.DecisionEngine
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
{
logger.Trace("Beginning size check for: {0}", subject);
var qualityType = _qualityTypeProvider.Get((int)subject.Quality.QualityType);
//Need to determine if this is a 30 or 60 minute episode
@ -33,7 +34,10 @@ namespace NzbDrone.Core.Providers.DecisionEngine
//0 will be treated as unlimited
if (qualityType.MaxSize == 0)
{
logger.Trace("Max size is 0 (unlimited) - skipping check.");
return true;
}
var maxSize = qualityType.MaxSize.Megabytes();
var series = subject.Series;
@ -57,8 +61,12 @@ namespace NzbDrone.Core.Providers.DecisionEngine
//If the parsed size is greater than maxSize we don't want it
if (subject.Size > maxSize)
{
logger.Trace("Item: {0}, Size: {1} is greater than maximum allowed size ({1}), rejecting.", subject, subject.Size, maxSize);
return false;
}
logger.Trace("Item: {0}, meets size contraints.", subject);
return true;
}

View File

@ -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;
}
}
}

View File

@ -1,31 +1,19 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class QualityUpgradeSpecification
public class QualityAllowedByProfileSpecification
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public virtual bool IsSatisfiedBy(Quality currentQuality, Quality newQuality, QualityTypes cutOff)
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
{
if (currentQuality >= newQuality)
logger.Trace("Checking if report meets quality requirements. {0}", subject.Quality);
if (!subject.Series.QualityProfile.Allowed.Contains(subject.Quality.QualityType))
{
logger.Trace("existing item has better or equal quality. skipping");
return false;
}
if (currentQuality.QualityType == newQuality.QualityType && newQuality.Proper)
{
logger.Trace("Upgrading existing item to proper.");
return true;
}
if (currentQuality.QualityType >= cutOff)
{
logger.Trace("Existing item meets cut-off. skipping.");
logger.Trace("Quality {0} rejected by Series' quality profile", subject.Quality);
return false;
}

View File

@ -1,19 +1,31 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class QualityAllowedByProfileSpecification
public class QualityUpgradeSpecification
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
public virtual bool IsSatisfiedBy(Quality currentQuality, Quality newQuality, QualityTypes cutOff)
{
logger.Trace("Checking if report meets quality requirements. {0}", subject.Quality);
if (!subject.Series.QualityProfile.Allowed.Contains(subject.Quality.QualityType))
if (currentQuality >= newQuality)
{
logger.Trace("Quality {0} rejected by Series' quality profile", subject.Quality);
logger.Trace("existing item has better or equal quality. skipping");
return false;
}
if (currentQuality.QualityType == newQuality.QualityType && newQuality.Proper)
{
logger.Trace("Upgrading existing item to proper.");
return true;
}
if (currentQuality.QualityType >= cutOff)
{
logger.Trace("Existing item meets cut-off. skipping.");
return false;
}

View File

@ -253,6 +253,33 @@ namespace NzbDrone.Core.Providers
}
}
public virtual void CleanUpDropFolder(string path)
{
//Todo: We should rename files before importing them to prevent this issue from ever happening
var filesOnDisk = GetVideoFiles(path);
foreach(var file in filesOnDisk)
{
try
{
var episodeFile = _mediaFileProvider.GetFileByPath(file);
if (episodeFile != null)
{
Logger.Trace("[{0}] was imported but not moved, moving it now", file);
MoveEpisodeFile(episodeFile, true);
}
}
catch (Exception ex)
{
Logger.WarnException("Failed to move epiosde file from drop folder: " + file, ex);
throw;
}
}
}
private List<string> GetVideoFiles(string path)
{

View File

@ -46,26 +46,35 @@ namespace NzbDrone.Core.Providers.DownloadClients
public virtual bool IsInQueue(EpisodeParseResult newParseResult)
{
var queue = GetQueue().Where(c => c.ParseResult != null);
var matchigTitle = queue.Where(q => String.Equals(q.ParseResult.CleanTitle, newParseResult.Series.CleanTitle, StringComparison.InvariantCultureIgnoreCase));
var matchingTitleWithQuality = matchigTitle.Where(q => q.ParseResult.Quality >= newParseResult.Quality);
if (newParseResult.Series.IsDaily)
try
{
return matchingTitleWithQuality.Any(q => q.ParseResult.AirDate.Value.Date == newParseResult.AirDate.Value.Date);
var queue = GetQueue().Where(c => c.ParseResult != null);
var matchigTitle = queue.Where(q => String.Equals(q.ParseResult.CleanTitle, newParseResult.Series.CleanTitle, StringComparison.InvariantCultureIgnoreCase));
var matchingTitleWithQuality = matchigTitle.Where(q => q.ParseResult.Quality >= newParseResult.Quality);
if (newParseResult.Series.IsDaily)
{
return matchingTitleWithQuality.Any(q => q.ParseResult.AirDate.Value.Date == newParseResult.AirDate.Value.Date);
}
var matchingSeason = matchingTitleWithQuality.Where(q => q.ParseResult.SeasonNumber == newParseResult.SeasonNumber);
if (newParseResult.FullSeason)
{
return matchingSeason.Any();
}
return matchingSeason.Any(q => q.ParseResult.EpisodeNumbers != null && q.ParseResult.EpisodeNumbers.Any(e => newParseResult.EpisodeNumbers.Contains(e)));
}
var matchingSeason = matchingTitleWithQuality.Where(q => q.ParseResult.SeasonNumber == newParseResult.SeasonNumber);
if (newParseResult.FullSeason)
catch (Exception ex)
{
return matchingSeason.Any();
logger.WarnException("Unable to connect to SABnzbd to check queue.", ex);
return false;
}
return matchingSeason.Any(q => q.ParseResult.EpisodeNumbers != null && q.ParseResult.EpisodeNumbers.Any(e => newParseResult.EpisodeNumbers.Contains(e)));
}
public virtual bool DownloadNzb(string url, string title)

View File

@ -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);

View File

@ -328,7 +328,7 @@ namespace NzbDrone.Core.Providers
episodeToUpdate.SeasonNumber = episode.SeasonNumber;
episodeToUpdate.Title = episode.EpisodeName;
episodeToUpdate.Overview = episode.Overview.Truncate(4000);
episodeToUpdate.Overview = episode.Overview.Truncate(3500);
if (episode.FirstAired.Year > 1900)
episodeToUpdate.AirDate = episode.FirstAired.Date;

View File

@ -33,7 +33,7 @@ namespace NzbDrone.Core.Providers.ExternalNotification
if (_configProvider.TwitterNotifyOnDownload)
{
_logger.Trace("Sending Notification to Twitter (On Grab)");
_twitterProvider.SendTweet("Downloaded Complete: " + message);
_twitterProvider.SendTweet("Download Completed: " + message);
}
}

View File

@ -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,7 +236,13 @@ namespace NzbDrone.Core.Providers.Indexer
var title = TitlePreParser(item);
var episodeParseResult = Parser.ParseTitle(title);
if (episodeParseResult != null) episodeParseResult.Age = DateTime.Now.Date.Subtract(item.PublishDate.Date).Days;
if (episodeParseResult != null)
{
episodeParseResult.Age = DateTime.Now.Date.Subtract(item.PublishDate.Date).Days;
episodeParseResult.OriginalString = title;
}
_logger.Trace("Parsed: {0} from: {1}", episodeParseResult, item.Title.Text);
return CustomParser(item, episodeParseResult);
}

View File

@ -43,7 +43,7 @@ namespace NzbDrone.Core.Providers.Indexer
protected override string NzbDownloadUrl(SyndicationItem item)
{
return item.Links[1].Uri.ToString();
return item.Links[0].Uri.ToString();
}
protected override IList<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber)

View File

@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Syndication;
using System.Threading;
using System.Xml;
using NLog;
@ -60,7 +61,11 @@ namespace NzbDrone.Core.Providers.Indexer
logger.WarnException("Unable to parse Feed date " + dateVal, e);
}
dateVal = parsedDate.ToString(CultureInfo.CurrentCulture.DateTimeFormat.RFC1123Pattern);
var currentCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
dateVal = parsedDate.ToString("ddd, dd MMM yyyy HH:mm:ss zzz");
dateVal = dateVal.Remove(dateVal.LastIndexOf(':'), 1);
Thread.CurrentThread.CurrentCulture = currentCulture;
}
return dateVal;

View File

@ -52,6 +52,11 @@ namespace NzbDrone.Core.Providers
return _database.Exists<EpisodeFile>("WHERE Path =@0", path.NormalizePath());
}
public virtual EpisodeFile GetFileByPath(string path)
{
return _database.SingleOrDefault<EpisodeFile>("WHERE Path =@0", path.NormalizePath());
}
public virtual EpisodeFile GetEpisodeFile(int episodeFileId)
{
return _database.Single<EpisodeFile>(episodeFileId);
@ -133,7 +138,7 @@ namespace NzbDrone.Core.Providers
if (updated > 0)
{
Logger.Debug("Removed {0} orphan file(s) from database.S", updated);
Logger.Debug("Removed {0} orphan file(s) from database.", updated);
}
}

View File

@ -66,6 +66,8 @@ namespace NzbDrone.Core.Providers
return;
}
_diskScanProvider.CleanUpDropFolder(subfolderInfo.FullName);
var importedFiles = _diskScanProvider.Scan(series, subfolderInfo.FullName);
importedFiles.ForEach(file => _diskScanProvider.MoveEpisodeFile(file, true));

View File

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using Ninject;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Search;
using PetaPoco;
namespace NzbDrone.Core.Providers
{
public class SearchHistoryProvider
{
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 SearchHistoryProvider(IDatabase database, SeriesProvider seriesProvider,
DownloadProvider downloadProvider, EpisodeProvider episodeProvider)
{
_database = database;
_seriesProvider = seriesProvider;
_downloadProvider = downloadProvider;
_episodeProvider = episodeProvider;
}
public SearchHistoryProvider()
{
}
public virtual int Add(SearchHistory searchHistory)
{
logger.Trace("Adding new search result");
searchHistory.SuccessfulDownload = searchHistory.SearchHistoryItems.Any(s => s.Success);
var id = Convert.ToInt32(_database.Insert(searchHistory));
searchHistory.SearchHistoryItems.ForEach(s => s.SearchHistoryId = id);
logger.Trace("Adding search result items");
_database.InsertMany(searchHistory.SearchHistoryItems);
return id;
}
public virtual void Delete(int id)
{
logger.Trace("Deleting search result items attached to: {0}", id);
_database.Execute("DELETE FROM SearchHistoryItems WHERE SearchHistoryId = @0", id);
logger.Trace("Deleting search result: {0}", id);
_database.Delete<SearchHistory>(id);
}
public virtual List<SearchHistory> AllSearchHistory()
{
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(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 = SearchHistory.SeriesId
LEFT JOIN Episodes
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<SearchHistory>(sql);
}
public virtual SearchHistory GetSearchHistory(int id)
{
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 SearchHistory
INNER JOIN Series
ON Series.SeriesId = SearchHistory.SeriesId
LEFT JOIN Episodes
ON Episodes.EpisodeId = SearchHistory.EpisodeId
WHERE SearchHistory.Id = @0";
var result = _database.Single<SearchHistory>(sql, id);
result.SearchHistoryItems = _database.Fetch<SearchHistoryItem>("WHERE SearchHistoryId = @0", id);
return result;
}
public virtual void ForceDownload(int itemId)
{
var item = _database.Single<SearchHistoryItem>(itemId);
logger.Info("Starting Force Download of: {0}", item.ReportTitle);
var searchResult = _database.Single<SearchHistory>(item.SearchHistoryId);
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);
logger.Info("Forcing Download of: {0}", item.ReportTitle);
_downloadProvider.DownloadReport(parseResult);
}
}
}

View File

@ -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 SearchHistoryProvider _searchHistoryProvider;
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,
SearchHistoryProvider searchHistoryProvider)
{
_episodeProvider = episodeProvider;
_downloadProvider = downloadProvider;
@ -36,6 +39,7 @@ namespace NzbDrone.Core.Providers
_sceneMappingProvider = sceneMappingProvider;
_upgradePossibleSpecification = upgradePossibleSpecification;
_allowedDownloadSpecification = allowedDownloadSpecification;
_searchHistoryProvider = searchHistoryProvider;
}
public SearchProvider()
@ -44,13 +48,20 @@ namespace NzbDrone.Core.Providers
public virtual bool SeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber)
{
var searchResult = new SearchHistory
{
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,19 +91,20 @@ namespace NzbDrone.Core.Providers
e => e.EpisodeNumbers = episodeNumbers.ToList()
);
var downloadedEpisodes = ProcessSearchResults(notification, reports, series, seasonNumber);
searchResult.SearchHistoryItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber);
_searchHistoryProvider.Add(searchResult);
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.Successes.Count == episodeNumbers.Count);
}
public virtual List<int> 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 SearchHistory
{
SearchTime = DateTime.Now,
SeriesId = seriesId,
SeasonNumber = seasonNumber
};
var series = _seriesProvider.GetSeries(seriesId);
@ -107,19 +119,18 @@ namespace NzbDrone.Core.Providers
return new List<int>();
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<int>();
notification.CurrentMessage = "Processing search results";
searchResult.SearchHistoryItems = ProcessSearchResults(notification, reports, searchResult, series, seasonNumber);
return ProcessSearchResults(notification, reports, series, seasonNumber);
_searchHistoryProvider.Add(searchResult);
return searchResult.Successes;
}
public virtual bool EpisodeSearch(ProgressNotification notification, int episodeId)
@ -136,7 +147,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 +156,39 @@ 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 SearchHistory
{
SearchTime = DateTime.Now,
SeriesId = episode.Series.SeriesId
};
var reports = PerformSearch(notification, episode.Series, episode.SeasonNumber, new List<Episode> { 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.SearchHistoryItems = ProcessSearchResults(notification, reports, episode.Series, episode.AirDate.Value);
_searchHistoryProvider.Add(searchResult);
if (episode.Series.IsDaily && ProcessSearchResults(notification, reports, episode.Series, episode.AirDate.Value))
if (searchResult.SearchHistoryItems.Any(r => r.Success))
return true;
}
else
{
searchResult.EpisodeId = episodeId;
searchResult.SearchHistoryItems = ProcessSearchResults(notification, reports, searchResult, episode.Series, episode.SeasonNumber, episode.EpisodeNumber);
_searchHistoryProvider.Add(searchResult);
if (searchResult.SearchHistoryItems.Any(r => r.Success))
return true;
}
Logger.Warn("Unable to find {0} in any of indexers.", episode);
@ -170,7 +201,6 @@ namespace NzbDrone.Core.Providers
notification.CurrentMessage = String.Format("Sorry, couldn't find you {0} in any of indexers.", episode);
}
return false;
}
@ -227,9 +257,10 @@ namespace NzbDrone.Core.Providers
return reports;
}
public List<int> ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, Series series, int seasonNumber, int? episodeNumber = null)
public List<SearchHistoryItem> ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, SearchHistory searchResult, Series series, int seasonNumber, int? episodeNumber = null)
{
var successes = new List<int>();
var items = new List<SearchHistoryItem>();
foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality).ThenBy(c => c.Age))
{
@ -237,6 +268,20 @@ namespace NzbDrone.Core.Providers
{
Logger.Trace("Analysing report " + episodeParseResult);
var item = new SearchHistoryItem
{
ReportTitle = episodeParseResult.OriginalString,
NzbUrl = episodeParseResult.NzbUrl,
Indexer = episodeParseResult.Indexer,
Quality = episodeParseResult.Quality.QualityType,
Proper = episodeParseResult.Quality.Proper,
Size = episodeParseResult.Size,
Age = episodeParseResult.Age,
Language = episodeParseResult.Language
};
items.Add(item);
//Get the matching series
episodeParseResult.Series = _seriesProvider.FindSeries(episodeParseResult.CleanTitle);
@ -244,6 +289,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 +297,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 +305,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 +313,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))
item.SearchError = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult);
if (item.SearchError == ReportRejectionType.None)
{
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
try
@ -279,12 +329,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 +350,38 @@ namespace NzbDrone.Core.Providers
}
}
return successes;
return items;
}
public bool ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, Series series, DateTime airDate)
public List<SearchHistoryItem> ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, Series series, DateTime airDate)
{
var items = new List<SearchHistoryItem>();
var skip = false;
foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality))
{
try
{
var item = new SearchHistoryItem
{
ReportTitle = episodeParseResult.OriginalString,
NzbUrl = episodeParseResult.NzbUrl,
Indexer = episodeParseResult.Indexer,
Quality = episodeParseResult.Quality.QualityType,
Proper = episodeParseResult.Quality.Proper,
Size = episodeParseResult.Size,
Age = episodeParseResult.Age,
Language = episodeParseResult.Language
};
items.Add(item);
if (skip)
{
item.SearchError = ReportRejectionType.Skipped;
continue;
}
Logger.Trace("Analysing report " + episodeParseResult);
//Get the matching series
@ -310,13 +389,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))
item.SearchError = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult);
if (item.SearchError == ReportRejectionType.None)
{
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
try
@ -327,13 +413,19 @@ 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)
{
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;
}
}
}
@ -342,7 +434,8 @@ namespace NzbDrone.Core.Providers
Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e);
}
}
return false;
return items;
}
private List<int> GetEpisodeNumberPrefixes(IEnumerable<int> episodeNumbers)

View File

@ -21,7 +21,7 @@ namespace NzbDrone.Core.Providers
_configProvider = configProvider;
}
public virtual bool SendEmail(string subject, string body, bool htmlBody = false)
public virtual void SendEmail(string subject, string body, bool htmlBody = false)
{
//Create the Email message
var email = new MailMessage();
@ -54,7 +54,14 @@ namespace NzbDrone.Core.Providers
credentials = new NetworkCredential(username, password);
//Send the email
return Send(email, _configProvider.SmtpServer, _configProvider.SmtpPort, _configProvider.SmtpUseSsl, credentials);
try
{
Send(email, _configProvider.SmtpServer, _configProvider.SmtpPort, _configProvider.SmtpUseSsl, credentials);
}
catch(Exception ex)
{
Logger.Error("Error sending email. Subject: {0}", email.Subject);
}
}
public virtual bool SendTestEmail(string server, int port, bool ssl, string username, string password, string fromAddress, string toAddresses)
@ -90,11 +97,19 @@ namespace NzbDrone.Core.Providers
credentials = new NetworkCredential(username, password);
//Send the email
return Send(email, server, port, ssl, credentials);
try
{
Send(email, server, port, ssl, credentials);
}
catch(Exception ex)
{
Logger.TraceException("Failed to send test email", ex);
return false;
}
return true;
}
//TODO: make this throw instead of return false.
public virtual bool Send(MailMessage email, string server, int port, bool ssl, NetworkCredential credentials)
public virtual void Send(MailMessage email, string server, int port, bool ssl, NetworkCredential credentials)
{
try
{
@ -109,15 +124,12 @@ namespace NzbDrone.Core.Providers
//Send the email
smtp.Send(email);
return true;
}
catch (Exception ex)
{
Logger.Error("There was an error sending an email.");
Logger.TraceException(ex.Message, ex);
return false;
Logger.ErrorException("There was an error sending an email.", ex);
throw;
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject;
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository;
using PetaPoco;
namespace NzbDrone.Core.Providers
{
public class StatsProvider
{
private readonly IDatabase _database;
[Inject]
public StatsProvider(IDatabase database)
{
_database = database;
}
public StatsProvider()
{
}
public virtual StatsModel GetStats()
{
var series = _database.Fetch<Series>();
var episodes = _database.Fetch<Episode>();
var history = _database.Fetch<History>("WHERE Date <= @0", DateTime.Today.AddDays(-30));
var stats = new StatsModel();
stats.SeriesTotal = series.Count;
stats.SeriesContinuing = series.Count(s => s.Status == "Continuing");
stats.SeriesEnded = series.Count(s => s.Status == "Ended");
stats.EpisodesTotal = episodes.Count;
stats.EpisodesOnDisk = episodes.Count(e => e.EpisodeFileId > 0);
stats.EpisodesMissing = episodes.Count(e => e.Ignored == false && e.EpisodeFileId == 0);
stats.DownloadedLastMonth = history.Count;
stats.DownloadLastWeek = history.Count(h => h.Date <= DateTime.Today.AddDays(7));
return stats;
}
}
}

View File

@ -0,0 +1,48 @@
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("SearchHistory")]
public class SearchHistory
{
public int Id { get; set; }
public int SeriesId { get; set; }
public int? SeasonNumber { get; set; }
public int? EpisodeId { get; set; }
public DateTime SearchTime { get; set; }
public bool SuccessfulDownload { get; set; }
[ResultColumn]
public List<SearchHistoryItem> SearchHistoryItems { get; set; }
[Ignore]
public List<int> Successes { 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 SuccessfulCount { get; set; }
}
}

View File

@ -0,0 +1,33 @@
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("SearchHistoryItems")]
public class SearchHistoryItem
{
public int Id { get; set; }
public int SearchHistoryId { 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 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);
}
}
}

View File

@ -6,7 +6,7 @@
<package id="Growl" version="0.6" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" />
<package id="MiniProfiler" version="1.9" />
<package id="Newtonsoft.Json" version="4.0.8" />
<package id="Newtonsoft.Json" version="4.5.3" />
<package id="Ninject" version="2.2.1.4" />
<package id="NLog" version="2.0.0.2000" />
<package id="SignalR.Hosting.AspNet" version="0.4.0.0" />

View File

@ -1,8 +1,7 @@
using MongoDB.Driver;
using NzbDrone.Services.Service.Datastore;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Mvc;
using NzbDrone.Services.Service.Datastore;
using NzbDrone.Services.Service.Migrations;
using Services.PetaPoco;
@ -33,8 +32,6 @@ namespace NzbDrone.Services.Service.App_Start
var kernel = new StandardKernel();
MigrationsHelper.Run(Connection.GetConnectionString);
kernel.Bind<IDatabase>().ToMethod(c => Connection.GetPetaPocoDb());
kernel.Bind<MongoDatabase>().ToConstant(Connection.GetMongoDb());
return kernel;
}

View File

@ -1,153 +1,23 @@
using System;
using System.Linq;
using System.Linq;
using System.Web.Mvc;
using MongoDB.Driver;
using NLog;
using Ninject;
using NzbDrone.Common.Contract;
using NzbDrone.Services.Service.Exceptions;
using NzbDrone.Services.Service.Repository.Reporting;
using Services.PetaPoco;
using ExceptionInstance = NzbDrone.Services.Service.Repository.Reporting.ExceptionInstance;
using ExceptionReport = NzbDrone.Common.Contract.ExceptionReport;
namespace NzbDrone.Services.Service.Controllers
{
public class ExceptionController : Controller
{
private readonly IDatabase _database;
private readonly ExceptionRepository _exceptionRepository;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public ExceptionController(IDatabase database, ExceptionRepository exceptionRepository)
{
_database = database;
_exceptionRepository = exceptionRepository;
}
[HttpPost]
public EmptyResult ReportExisting(ExistingExceptionReport existingExceptionReport)
public EmptyResult ReportExisting()
{
try
{
if (ExceptionHashExists(existingExceptionReport.Hash))
{
var exceptionInstance = new ExceptionInstance
{
ExceptionHash = existingExceptionReport.Hash,
IsProduction = existingExceptionReport.IsProduction,
LogMessage = existingExceptionReport.LogMessage,
UGuid = existingExceptionReport.UGuid,
Timestamp = DateTime.Now
};
_database.Insert(exceptionInstance);
}
else
{
logger.Warn("Invalid exception hash '{0}'", existingExceptionReport.Hash);
}
}
catch (Exception e)
{
logger.FatalException("Error has occurred while saving exception", e);
throw;
}
return new EmptyResult();
}
[HttpPost]
public JsonResult ReportNew(ExceptionReport exceptionReport)
public JsonResult ReportNew()
{
try
{
var report = new Exceptions.ExceptionReport();
report.AppVersion = exceptionReport.Version;
report.ApplicationId = "NzbDrone";
report.ExceptionMessage = exceptionReport.ExceptionMessage;
report.ExceptionType = exceptionReport.Type;
report.Location = exceptionReport.Logger;
report.Message = exceptionReport.LogMessage;
report.Stack = exceptionReport.Stack;
report.Uid = exceptionReport.UGuid.ToString();
_exceptionRepository.Store(report);
var exceptionHash = GetExceptionDetailId(exceptionReport);
var exceptionInstance = new ExceptionInstance
{
ExceptionHash = exceptionHash,
IsProduction = exceptionReport.IsProduction,
LogMessage = exceptionReport.LogMessage,
Timestamp = DateTime.Now,
UGuid = exceptionReport.UGuid
};
_database.Insert(exceptionInstance);
return new JsonResult { Data = new ExceptionReportResponse { ExceptionHash = exceptionHash } };
}
catch (Exception e)
{
logger.FatalException("Error has occurred while saving exception", e);
if (!exceptionReport.IsProduction)
{
throw;
}
}
return new JsonResult();
}
private string GetExceptionDetailId(ExceptionReport exceptionReport)
{
var reportHash = Hash(String.Concat(exceptionReport.Version, exceptionReport.String, exceptionReport.Logger));
if (!ExceptionHashExists(reportHash))
{
var exeptionDetail = new ExceptionDetail();
exeptionDetail.Hash = reportHash;
exeptionDetail.Logger = exceptionReport.Logger;
exeptionDetail.String = exceptionReport.String;
exeptionDetail.Type = exceptionReport.Type;
exeptionDetail.Version = exceptionReport.Version;
_database.Insert(exeptionDetail);
}
return reportHash;
}
private bool ExceptionHashExists(string reportHash)
{
return _database.Exists<ExceptionDetail>(reportHash);
}
private static string Hash(string input)
{
uint mCrc = 0xffffffff;
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(input);
foreach (byte myByte in bytes)
{
mCrc ^= ((uint)(myByte) << 24);
for (var i = 0; i < 8; i++)
{
if ((Convert.ToUInt32(mCrc) & 0x80000000) == 0x80000000)
{
mCrc = (mCrc << 1) ^ 0x04C11DB7;
}
else
{
mCrc <<= 1;
}
}
}
return String.Format("{0:x8}", mCrc);
}
}
}

View File

@ -63,9 +63,9 @@ namespace NzbDrone.Services.Service.Controllers
}
[HttpPost]
public JsonResult ReportException(ExceptionReport exceptionReport)
public JsonResult ReportException()
{
return _exceptionController.ReportNew(exceptionReport);
return new JsonResult();
}
}
}

View File

@ -1,9 +1,5 @@
using System;
using System.Configuration;
using System.Configuration;
using System.Linq;
using MongoDB.Bson;
using MongoDB.Driver;
using NzbDrone.Services.Service.Migrations;
using Services.PetaPoco;
@ -28,22 +24,6 @@ namespace NzbDrone.Services.Service.Datastore
}
public static MongoDatabase GetMongoDb()
{
var serverSettings = new MongoServerSettings()
{
ConnectionMode = ConnectionMode.Direct,
ConnectTimeout = TimeSpan.FromSeconds(10),
DefaultCredentials = new MongoCredentials("nzbdrone", "nzbdronepassword"),
GuidRepresentation = GuidRepresentation.Standard,
Server = new MongoServerAddress("ds031747.mongolab.com", 31747),
SafeMode = new SafeMode(true) { J = true },
};
var server = MongoServer.Create(serverSettings);
return server.GetDatabase("nzbdrone_ex");
}
}
}

View File

@ -1,29 +0,0 @@
using System.Linq;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
namespace NzbDrone.Services.Service.Exceptions
{
public class ExceptionInfo
{
public ExceptionInfo()
{
Instances = new ExceptionInstance[0];
}
[BsonId]
public string Hash { get; set; }
[BsonElement("xtype")]
public string ExceptionType { get; set; }
[BsonElement("stk")]
public string Stack { get; set; }
[BsonElement("loc")]
public string Location { get; set; }
[BsonElement("inst")]
public IEnumerable<ExceptionInstance> Instances { get; set; }
}
}

View File

@ -1,24 +0,0 @@
using System.Linq;
using System;
using MongoDB.Bson.Serialization.Attributes;
namespace NzbDrone.Services.Service.Exceptions
{
public class ExceptionInstance
{
[BsonElement("ver")]
public string AppVersion { get; set; }
[BsonElement("uid")]
public string UserId { get; set; }
[BsonElement("xmsg")]
public string ExceptionMessage { get; set; }
[BsonElement("msg")]
public string Message { get; set; }
[BsonElement("time")]
public DateTime Time { get; set; }
}
}

View File

@ -1,16 +0,0 @@
using System.Linq;
namespace NzbDrone.Services.Service.Exceptions
{
public class ExceptionReport
{
public string ApplicationId { get; set; }
public string AppVersion { get; set; }
public string Uid { get; set; }
public string ExceptionType { get; set; }
public string ExceptionMessage { get; set; }
public string Stack { get; set; }
public string Location { get; set; }
public string Message { get; set; }
}
}

View File

@ -1,92 +0,0 @@
using System;
using System.Linq;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
namespace NzbDrone.Services.Service.Exceptions
{
public class ExceptionRepository
{
private readonly MongoDatabase _mongoDb;
public ExceptionRepository(MongoDatabase mongoDb)
{
_mongoDb = mongoDb;
}
public ExceptionRepository()
{
}
public virtual string Store(NzbDrone.Services.Service.Exceptions.ExceptionReport exceptionReport)
{
var hash = GetExceptionDetailId(exceptionReport);
var exceptionInstance = new NzbDrone.Services.Service.Exceptions.ExceptionInstance
{
AppVersion = exceptionReport.AppVersion,
ExceptionMessage = exceptionReport.ExceptionMessage,
Message = exceptionReport.Message,
Time = DateTime.UtcNow,
UserId = exceptionReport.Uid
};
var applicationExceptions = _mongoDb.GetCollection(exceptionReport.ApplicationId);
applicationExceptions.Update(Query.EQ("_id", hash), Update.PushWrapped("inst", exceptionInstance));
return hash;
}
private string GetExceptionDetailId(NzbDrone.Services.Service.Exceptions.ExceptionReport exceptionReport)
{
var hash = Hash(String.Concat(exceptionReport.AppVersion, exceptionReport.Location, exceptionReport.ExceptionType, exceptionReport.Stack));
if (!ExceptionInfoExists(exceptionReport.ApplicationId, hash))
{
var exceptionInfo = new NzbDrone.Services.Service.Exceptions.ExceptionInfo
{
Hash = hash,
Stack = exceptionReport.Stack,
ExceptionType = exceptionReport.ExceptionType,
Location = exceptionReport.Location
};
_mongoDb.GetCollection(exceptionReport.ApplicationId).Insert(exceptionInfo);
}
return hash;
}
public bool ExceptionInfoExists(string applicationId, string hash)
{
var appCollection = _mongoDb.GetCollection(applicationId);
return appCollection.FindAs<NzbDrone.Services.Service.Exceptions.ExceptionInfo>(Query.EQ("_id", hash)).Any();
}
private static string Hash(string input)
{
uint mCrc = 0xffffffff;
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(input);
foreach (byte myByte in bytes)
{
mCrc ^= ((uint)(myByte) << 24);
for (var i = 0; i < 8; i++)
{
if ((Convert.ToUInt32(mCrc) & 0x80000000) == 0x80000000)
{
mCrc = (mCrc << 1) ^ 0x04C11DB7;
}
else
{
mCrc <<= 1;
}
}
}
return String.Format("{0:x8}", mCrc);
}
}
}

View File

@ -20,6 +20,8 @@
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>4.0</OldToolsVersion>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -41,9 +43,6 @@
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="AutoMapper">
<HintPath>..\..\packages\AutoMapper.2.0.0\lib\net40-client\AutoMapper.dll</HintPath>
</Reference>
<Reference Include="Elmah">
<HintPath>..\..\packages\elmah.corelibrary.1.2.1\lib\Elmah.dll</HintPath>
</Reference>
@ -64,14 +63,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Libraries\Migrator.NET\Migrator.Providers.dll</HintPath>
</Reference>
<Reference Include="MongoDB.Bson">
<HintPath>..\..\packages\mongocsharpdriver.1.4\lib\net35\MongoDB.Bson.dll</HintPath>
</Reference>
<Reference Include="MongoDB.Driver">
<HintPath>..\..\packages\mongocsharpdriver.1.4\lib\net35\MongoDB.Driver.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.0.8.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.4.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Newtonsoft.Json.4.5.3\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Ninject, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<HintPath>..\..\packages\Ninject.2.2.1.4\lib\net40-Full\Ninject.dll</HintPath>
@ -100,8 +94,6 @@
<Reference Include="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
@ -222,15 +214,9 @@
<Compile Include="App_Start\Logging.cs" />
<Compile Include="App_Start\NinjectMVC3.cs" />
<Compile Include="Controllers\ExceptionController.cs" />
<Compile Include="Exceptions\ExceptionInfo.cs" />
<Compile Include="Exceptions\ExceptionInstance.cs" />
<Compile Include="Exceptions\ExceptionReport.cs" />
<Compile Include="Exceptions\ExceptionRepository.cs" />
<Compile Include="Helpers\HtmlIncludeExtentions.cs" />
<Compile Include="Migrations\Migration20120226.cs" />
<Compile Include="Migrations\Migration20120229.cs" />
<Compile Include="Repository\Reporting\ExceptionDetail.cs" />
<Compile Include="Repository\Reporting\ExceptionInstance.cs" />
<Compile Include="Services.PetaPoco.cs" />
<Compile Include="Datastore\Connection.cs" />
<Compile Include="Controllers\DailySeriesController.cs" />
@ -252,7 +238,6 @@
<Compile Include="Providers\SceneMappingProvider.cs" />
<Compile Include="Repository\DailySeries.cs" />
<Compile Include="Repository\PendingSceneMapping.cs" />
<Compile Include="Repository\Reporting\ExceptionRow.cs" />
<Compile Include="Repository\Reporting\ParseErrorRow.cs" />
<Compile Include="Repository\Reporting\ReportRowBase.cs" />
<Compile Include="Repository\SceneMapping.cs" />
@ -308,6 +293,7 @@
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -1,16 +0,0 @@
using System.Linq;
using Services.PetaPoco;
namespace NzbDrone.Services.Service.Repository.Reporting
{
[TableName("Exceptions")]
[PrimaryKey("Hash", autoIncrement = false)]
public class ExceptionDetail
{
public string Hash { get; set; }
public string Logger { get; set; }
public string Type { get; set; }
public string String { get; set; }
public string Version { get; set; }
}
}

View File

@ -1,17 +0,0 @@
using System;
using System.Linq;
using Services.PetaPoco;
namespace NzbDrone.Services.Service.Repository.Reporting
{
[TableName("ExceptionInstances")]
public class ExceptionInstance
{
public long Id { get; set; }
public string ExceptionHash { get; set; }
public string LogMessage { get; set; }
public DateTime Timestamp { get; set; }
public bool IsProduction { get; set; }
public Guid UGuid { get; set; }
}
}

View File

@ -1,14 +0,0 @@
using System.Linq;
using Services.PetaPoco;
namespace NzbDrone.Services.Service.Repository.Reporting
{
[TableName("ExceptionReports")]
public class ExceptionRow : ReportRowBase
{
public string Type { get; set; }
public string Logger { get; set; }
public string LogMessage { get; set; }
public string String { get; set; }
}
}

View File

@ -74,7 +74,7 @@
{ sWidth: 'auto', "mDataProp": "Title", "bSortable": false }, //Title
{ sWidth: '40px', "mDataProp": "Commands", "bSortable": false, "fnRender": function (row) {
var promoteImage = "<img src=\"../../Content/Images/Promote.png\" alt=\"Promote\" title=\Promote to Active\" class=\"gridAction\" onclick=\"promoteMapping(this.parentNode.parentNode, " + row.aData["MappingId"] + ")\">";
var deleteImage = "<img src=\"../../Content/Images/X.png\" alt=\"Delete\" title=\"Delete\" class=\"gridAction\" onclick=\"deleteMapping(this.parentNode.parentNode, " + row.aData["MappingId"] + ")\">";
var deleteImage = "<img src=\"../../Content/Images/close.png\" alt=\"Delete\" title=\"Delete\" class=\"gridAction\" onclick=\"deleteMapping(this.parentNode.parentNode, " + row.aData["MappingId"] + ")\">";
return promoteImage + deleteImage;
}

View File

@ -16,7 +16,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.8.0" newVersion="4.0.8.0" />
<bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="2.0.0" />
<package id="elmah" version="1.2.0.1" />
<package id="elmah.corelibrary" version="1.2.1" />
<package id="elmah.sqlserver" version="1.2" />
@ -9,8 +8,7 @@
<package id="jQuery.UI.Combined" version="1.8.17" />
<package id="jQuery.Validation" version="1.9" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" />
<package id="mongocsharpdriver" version="1.4" />
<package id="Newtonsoft.Json" version="4.0.8" />
<package id="Newtonsoft.Json" version="4.5.3" />
<package id="Ninject" version="2.2.1.4" />
<package id="Ninject.MVC3" version="2.2.2.0" />
<package id="NLog" version="2.0.0.2000" />

View File

@ -1,74 +0,0 @@
using System;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Contract;
using NzbDrone.Services.Service.Repository.Reporting;
using NzbDrone.Services.Tests.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Services.Tests.ExceptionControllerTests
{
[TestFixture]
public class ReportExistingFixture : ServicesTestBase
{
Service.Controllers.ExceptionController Controller
{
get
{
return Mocker.Resolve<Service.Controllers.ExceptionController>();
}
}
private static ExistingExceptionReport CreateExceptionReport()
{
return new ExistingExceptionReport
{
IsProduction = true,
Version = "1.1.2.323456",
UGuid = Guid.NewGuid(),
LogMessage = @"Log message",
Hash = "ABC123"
};
}
[Test]
public void should_log_warn_if_hash_doesnt_exist()
{
WithRealDb();
Controller.ReportExisting(CreateExceptionReport());
Db.Fetch<ExceptionDetail>().Should().BeEmpty();
Db.Fetch<ExceptionInstance>().Should().BeEmpty();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_save_instance_if_hash_is_valid()
{
WithRealDb();
var existing = CreateExceptionReport();
Db.Insert(Builder<ExceptionDetail>.CreateNew().With(c => c.Hash = existing.Hash).Build());
Controller.ReportExisting(existing);
Db.Fetch<ExceptionDetail>().Should().HaveCount(1);
var exceptionInstance = Db.Fetch<ExceptionInstance>();
exceptionInstance.Should().HaveCount(1);
exceptionInstance.Single().Id.Should().BeGreaterThan(0);
exceptionInstance.Single().ExceptionHash.Should().NotBeBlank();
exceptionInstance.Single().IsProduction.Should().Be(existing.IsProduction);
exceptionInstance.Single().Timestamp.Should().BeWithin(TimeSpan.FromSeconds(4)).Before(DateTime.Now);
exceptionInstance.Single().LogMessage.Should().Be(existing.LogMessage);
exceptionInstance.Single().UGuid.Should().Be(existing.UGuid);
}
}
}

View File

@ -1,213 +0,0 @@
using System;
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Contract;
using NzbDrone.Services.Service.Repository.Reporting;
using NzbDrone.Services.Tests.Framework;
namespace NzbDrone.Services.Tests.ExceptionControllerTests
{
[TestFixture]
public class ReportNewFixture : ServicesTestBase
{
Service.Controllers.ExceptionController Controller
{
get
{
return Mocker.Resolve<Service.Controllers.ExceptionController>();
}
}
private static ExceptionReport CreateExceptionReport()
{
return new ExceptionReport
{
IsProduction = true,
Version = "1.1.2.323456",
UGuid = Guid.NewGuid(),
Logger = "NzbDrone.Logger.Name",
LogMessage = @"Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message",
String = @"Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message",
Type = typeof(InvalidOperationException).Name
};
}
[Test]
public void ReportNew_should_save_instance()
{
var exceptionReport = CreateExceptionReport();
WithRealDb();
Controller.ReportNew(exceptionReport);
var exceptionInstance = Db.Fetch<ExceptionInstance>();
exceptionInstance.Should().HaveCount(1);
exceptionInstance.Single().Id.Should().BeGreaterThan(0);
exceptionInstance.Single().ExceptionHash.Should().NotBeBlank();
exceptionInstance.Single().IsProduction.Should().Be(exceptionReport.IsProduction);
exceptionInstance.Single().Timestamp.Should().BeWithin(TimeSpan.FromSeconds(4)).Before(DateTime.Now);
exceptionInstance.Single().LogMessage.Should().Be(exceptionReport.LogMessage);
exceptionInstance.Single().UGuid.Should().Be(exceptionReport.UGuid);
}
[Test]
public void ReportNew_should_save_detail()
{
var exceptionReport = CreateExceptionReport();
WithRealDb();
Controller.ReportNew(exceptionReport);
var exceptionDetails = Db.Fetch<ExceptionDetail>();
exceptionDetails.Should().HaveCount(1);
exceptionDetails.Single().Hash.Should().NotBeBlank();
exceptionDetails.Single().Logger.Should().Be(exceptionReport.Logger);
exceptionDetails.Single().Type.Should().Be(exceptionReport.Type);
exceptionDetails.Single().String.Should().Be(exceptionReport.String);
exceptionDetails.Single().Version.Should().Be(exceptionReport.Version);
}
[Test]
public void ReportNew_should_return_exception_id()
{
var exceptionReport = CreateExceptionReport();
WithRealDb();
var response = Controller.ReportNew(exceptionReport);
response.Data.Should().BeOfType<ExceptionReportResponse>();
((ExceptionReportResponse)response.Data).ExceptionHash.Should().NotBeBlank();
}
[Test]
public void Reporting_exception_more_than_once_should_create_single_detail_with_multiple_instances()
{
var exceptionReport = CreateExceptionReport();
WithRealDb();
var response1 = Controller.ReportNew(exceptionReport);
var response2 = Controller.ReportNew(exceptionReport);
var response3 = Controller.ReportNew(exceptionReport);
var detail = Db.Fetch<ExceptionDetail>();
var instances = Db.Fetch<ExceptionInstance>();
detail.Should().HaveCount(1);
instances.Should().HaveCount(3);
instances.Should().OnlyContain(c => c.ExceptionHash == detail.Single().Hash);
}
[Test]
public void Reporting_exception_with_diffrent_version_should_create_new_detail()
{
var exceptionReport1 = CreateExceptionReport();
exceptionReport1.Version = "0.1.1";
var exceptionReport2 = CreateExceptionReport();
exceptionReport2.Version = "0.2.1";
WithRealDb();
Controller.ReportNew(exceptionReport1);
Controller.ReportNew(exceptionReport2);
var detail = Db.Fetch<ExceptionDetail>();
var instances = Db.Fetch<ExceptionInstance>();
detail.Should().HaveCount(2);
instances.Should().HaveCount(2);
}
[Test]
public void Reporting_exception_with_diffrent_strting_should_create_new_detail()
{
var exceptionReport1 = CreateExceptionReport();
exceptionReport1.String = "Error1";
var exceptionReport2 = CreateExceptionReport();
exceptionReport2.String = "Error2";
WithRealDb();
Controller.ReportNew(exceptionReport1);
Controller.ReportNew(exceptionReport2);
var detail = Db.Fetch<ExceptionDetail>();
var instances = Db.Fetch<ExceptionInstance>();
detail.Should().HaveCount(2);
instances.Should().HaveCount(2);
}
[Test]
public void Reporting_exception_with_diffrent_logger_should_create_new_detail()
{
var exceptionReport1 = CreateExceptionReport();
exceptionReport1.Logger = "logger1";
var exceptionReport2 = CreateExceptionReport();
exceptionReport2.Logger = "logger2";
WithRealDb();
Controller.ReportNew(exceptionReport1);
Controller.ReportNew(exceptionReport2);
var detail = Db.Fetch<ExceptionDetail>();
var instances = Db.Fetch<ExceptionInstance>();
detail.Should().HaveCount(2);
instances.Should().HaveCount(2);
}
}
}

View File

@ -12,6 +12,8 @@
<AssemblyName>NzbDrone.Services.Tests</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -68,8 +70,6 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ExceptionControllerTests\ReportExistingFixture.cs" />
<Compile Include="ExceptionControllerTests\ReportNewFixture.cs" />
<Compile Include="ReportingControllerFixture.cs" />
<Compile Include="Framework\ServicesTestBase.cs" />
<Compile Include="Framework\TestDbHelper.cs" />
@ -100,6 +100,7 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -39,62 +39,6 @@ namespace NzbDrone.Services.Tests
};
}
private static ExceptionReport CreateExceptionReport()
{
return new ExceptionReport
{
IsProduction = true,
Version = "1.1.2.323456",
UGuid = Guid.NewGuid(),
Logger = "NzbDrone.Logger.Name",
LogMessage = @"Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message",
String = @"Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message",
Type = typeof(InvalidOperationException).Name
};
}
[Test]
public void parse_report_should_be_saved()
{

View File

@ -12,6 +12,8 @@
<AssemblyName>NzbDrone.Test.Common</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -82,6 +84,7 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -49,6 +49,8 @@ namespace NzbDrone.Test.Common
MockedRestProvider = new Mock<RestProvider>();
ReportingService.RestProvider = MockedRestProvider.Object;
ReportingService.SetupExceptronDriver();
if (Directory.Exists(TempFolder))
{

View File

@ -13,6 +13,8 @@
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
@ -47,6 +49,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -14,6 +14,8 @@
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
@ -87,6 +89,7 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -14,6 +14,8 @@
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
@ -61,6 +63,7 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -62,6 +62,7 @@ namespace NzbDrone.Update
private static void InitLoggers()
{
ReportingService.RestProvider = _kernel.Get<RestProvider>();
ReportingService.SetupExceptronDriver();
LogConfiguration.RegisterRemote();

View File

@ -12,6 +12,8 @@
<AssemblyName>NzbDrone.Web.UI.Automation</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -39,8 +41,9 @@
<Reference Include="Ionic.Zip, Version=1.9.1.8, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c, processorArchitecture=MSIL">
<HintPath>..\packages\DotNetZip.1.9.1.8\lib\net20\Ionic.Zip.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.0.6.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.4.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.4.5.1\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll</HintPath>
@ -53,8 +56,9 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="WebDriver, Version=2.19.0.0, Culture=neutral, PublicKeyToken=1c2bd1631853048f, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.WebDriver.2.19.0\lib\net40\WebDriver.dll</HintPath>
<Reference Include="WebDriver, Version=2.21.0.0, Culture=neutral, PublicKeyToken=1c2bd1631853048f, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Selenium.WebDriver.2.21.0\lib\net40\WebDriver.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@ -91,6 +95,7 @@
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -2,10 +2,10 @@
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30AD4FE6B2A6AEED" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.8.0" newVersion="4.0.6.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -2,7 +2,7 @@
<packages>
<package id="DotNetZip" version="1.9.1.8" />
<package id="FluentAssertions" version="1.7.0" />
<package id="Newtonsoft.Json" version="4.0.6" />
<package id="Newtonsoft.Json" version="4.5.1" />
<package id="NUnit" version="2.6.0.12054" />
<package id="Selenium.WebDriver" version="2.19.0" />
<package id="Selenium.WebDriver" version="2.21.0" />
</packages>

View File

@ -0,0 +1,17 @@
using DataTables.Mvc.Core.Helpers;
using DataTables.Mvc.Core.Models;
using System.Web.Mvc;
[assembly: WebActivator.PreApplicationStartMethod(typeof(NzbDrone.Web.App_Start.DataTablesModelBinderActivator), "Start")]
namespace NzbDrone.Web.App_Start
{
public static class DataTablesModelBinderActivator
{
public static void Start()
{
if (!ModelBinders.Binders.ContainsKey(typeof(DataTablesParams)))
ModelBinders.Binders.Add(typeof(DataTablesParams), new DataTablesModelBinder());
}
}
}

View File

@ -1,15 +0,0 @@
using System.Web.Mvc;
using System.Web.WebPages;
using NzbDrone.Web.Helpers;
using NzbDrone.Web.Models;
[assembly: WebActivator.PreApplicationStartMethod(typeof(NzbDrone.Web.App_Start.RegisterDatatablesModelBinder), "Start")]
namespace NzbDrone.Web.App_Start {
public static class RegisterDatatablesModelBinder {
public static void Start() {
if (!ModelBinders.Binders.ContainsKey(typeof(DataTablesParams)))
ModelBinders.Binders.Add(typeof(DataTablesParams), new DataTablesModelBinder());
}
}
}

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -4,6 +4,8 @@ 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.Web.Models;
@ -16,7 +18,8 @@ namespace NzbDrone.Web.Controllers
private readonly EnvironmentProvider _environmentProvider;
private readonly DiskProvider _diskProvider;
public LogController(LogProvider logProvider, EnvironmentProvider environmentProvider, DiskProvider diskProvider)
public LogController(LogProvider logProvider, EnvironmentProvider environmentProvider,
DiskProvider diskProvider)
{
_logProvider = logProvider;
_environmentProvider = environmentProvider;

View File

@ -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 SearchHistoryController : Controller
{
private readonly SearchHistoryProvider _searchHistoryProvider;
public SearchHistoryController(SearchHistoryProvider searchHistoryProvider)
{
_searchHistoryProvider = searchHistoryProvider;
}
public ActionResult Index()
{
var results = _searchHistoryProvider.AllSearchHistory();
var model = results.Select(s => new SearchHistoryModel
{
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 = _searchHistoryProvider.GetSearchHistory(searchId);
var model = new SearchDetailsModel
{
Id = searchResult.Id,
DisplayName = GetDisplayName(searchResult),
SearchHistoryItems =
searchResult.SearchHistoryItems.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)
{
_searchHistoryProvider.ForceDownload(id);
return new JsonResult { Data = "ok", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
public string GetDisplayName(SearchHistory 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);
}
}
}

View File

@ -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;
@ -21,16 +22,18 @@ namespace NzbDrone.Web.Controllers
private readonly ConfigProvider _configProvider;
private readonly DiskProvider _diskProvider;
private readonly BackupProvider _backupProvider;
private readonly StatsProvider _statsProvider;
public SystemController(JobProvider jobProvider, IndexerProvider indexerProvider,
ConfigProvider configProvider, DiskProvider diskProvider,
BackupProvider backupProvider)
BackupProvider backupProvider, StatsProvider statsProvider)
{
_jobProvider = jobProvider;
_indexerProvider = indexerProvider;
_configProvider = configProvider;
_diskProvider = diskProvider;
_backupProvider = backupProvider;
_statsProvider = statsProvider;
}
public ActionResult Jobs()
@ -129,7 +132,7 @@ namespace NzbDrone.Web.Controllers
foreach (var fileInfo in files)
{
fileResult += String.Format("<div><div style=\"width: 600px; display: inline-block;\">{0}</div><div style=\"display: inline-block;\">{1}</div></div>", fileInfo.Name,
FileSizeFormatHelper.Format(fileInfo.Length, 1));
fileInfo.Length.ToBestFileSize(1));
}
model.Files = fileResult;
@ -175,5 +178,12 @@ namespace NzbDrone.Web.Controllers
return File(fileInfo.FullName, "application/binary", fileInfo.Name);
}
public ActionResult Stats()
{
var model = _statsProvider.GetStats();
return View(model);
}
}
}

View File

@ -1,40 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NzbDrone.Web.Models;
namespace NzbDrone.Web.Helpers
{
/// <summary>
/// Model binder for datatables.js parameters a la http://geeksprogramando.blogspot.com/2011/02/jquery-datatables-plug-in-with-asp-mvc.html
/// </summary>
public class DataTablesModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
DataTablesParams obj = new DataTablesParams();
var request = controllerContext.HttpContext.Request.Params;
obj.iDisplayStart = Convert.ToInt32(request["iDisplayStart"]);
obj.iDisplayLength = Convert.ToInt32(request["iDisplayLength"]);
obj.iColumns = Convert.ToInt32(request["iColumns"]);
obj.sSearch = request["sSearch"];
obj.bEscapeRegex = Convert.ToBoolean(request["bEscapeRegex"]);
obj.iSortingCols = Convert.ToInt32(request["iSortingCols"]);
obj.sEcho = int.Parse(request["sEcho"]);
for (int i = 0; i < obj.iColumns; i++)
{
obj.bSortable.Add(Convert.ToBoolean(request["bSortable_" + i]));
obj.bSearchable.Add(Convert.ToBoolean(request["bSearchable_" + i]));
obj.sSearchColumns.Add(request["sSearch_" + i]);
obj.bEscapeRegexColumns.Add(Convert.ToBoolean(request["bEscapeRegex_" + i]));
obj.iSortCol.Add(Convert.ToInt32(request["iSortCol_" + i]));
obj.sSortDir.Add(request["sSortDir_" + i]);
}
return obj;
}
}
}

View File

@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace NzbDrone.Web.Models
{
public class DataTablesParams
{
public int iDisplayStart { get; set; }
public int iDisplayLength { get; set; }
public int iColumns { get; set; }
public string sSearch { get; set; }
public bool bEscapeRegex { get; set; }
public int iSortingCols { get; set; }
public int sEcho { get; set; }
public List<bool> bSortable { get; set; }
public List<bool> bSearchable { get; set; }
public List<string> sSearchColumns { get; set; }
public List<int> iSortCol { get; set; }
public List<string> sSortDir { get; set; }
public List<bool> bEscapeRegexColumns { get; set; }
public DataTablesParams()
{
bSortable = new List<bool>();
bSearchable = new List<bool>();
sSearchColumns = new List<string>();
iSortCol = new List<int>();
sSortDir = new List<string>();
bEscapeRegexColumns = new List<bool>();
}
}
}

View File

@ -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<SearchItemModel> SearchHistoryItems { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
namespace NzbDrone.Web.Models
{
public class SearchHistoryModel
{
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; }
}
}

View File

@ -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; }
}
}

View File

@ -23,6 +23,8 @@
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>4.0</OldToolsVersion>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -51,6 +53,10 @@
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="DataTables.Mvc.Core, Version=0.1.0.68, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\DataTables.Mvc.0.1.0.68\lib\DataTables.Mvc.Core.dll</HintPath>
</Reference>
<Reference Include="Dynamic">
<HintPath>..\packages\DynamicQuery.1.0\lib\35\Dynamic.dll</HintPath>
</Reference>
@ -65,8 +71,9 @@
<Reference Include="MvcMiniProfiler, Version=1.9.0.0, Culture=neutral, PublicKeyToken=b44f9351044011a3, processorArchitecture=MSIL">
<HintPath>..\packages\MiniProfiler.1.9\lib\net40\MvcMiniProfiler.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.0.8.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.4.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.4.5.3\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Ninject, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<HintPath>..\packages\Ninject.2.2.1.4\lib\net40-Full\Ninject.dll</HintPath>
@ -137,6 +144,8 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="App_Start\DataTablesMvc.cs" />
<Compile Include="Controllers\SearchHistoryController.cs" />
<Compile Include="Helpers\Validation\RequiredIfAnyAttribute.cs" />
<Compile Include="Helpers\Validation\RequiredIfAttribute.cs" />
<Content Include="Content\DataTables-1.9.0\media\css\jquery.dataTables.css" />
@ -150,6 +159,7 @@
<Content Include="Content\Images\AirsToday.png" />
<Content Include="Content\Images\background.jpg" />
<Content Include="Content\Images\blue.png" />
<Content Include="Content\Images\False.png" />
<Content Include="Content\Images\Gear.png" />
<Content Include="Content\Images\green.png" />
<Content Include="Content\Images\Indexers\FileSharingTalk.png" />
@ -159,6 +169,7 @@
<Content Include="Content\Images\Indexers\Nzbs.org.png" />
<Content Include="Content\Images\Indexers\WomblesIndex.png" />
<Content Include="Content\Images\logo.png" />
<Content Include="Content\Images\logo_small.png" />
<Content Include="Content\Images\pause.png" />
<Content Include="Content\Images\play.png" />
<Content Include="Content\Images\red.png" />
@ -166,6 +177,7 @@
<Content Include="Content\Images\settings.png" />
<Content Include="Content\Images\stop.png" />
<Content Include="Content\Images\thetvdb.png" />
<Content Include="Content\Images\True.png" />
<Content Include="Content\Images\yellow.png" />
<Content Include="Content\IndexerSettings.css" />
<Content Include="Content\jQueryUI\images\ui-bg_diagonals-thick_30_a32d00_40x40.png" />
@ -204,7 +216,6 @@
<Compile Include="App_Start\EntityFramework.SqlServerCompact.cs" />
<Compile Include="App_Start\Logging.cs" />
<Compile Include="App_Start\MiniProfiler.cs" />
<Compile Include="App_Start\RegisterDataTablesModelBinder.cs" />
<Compile Include="Filters\JsonErrorFilter.cs" />
<Compile Include="Controllers\CommandController.cs" />
<Compile Include="Controllers\DirectoryController.cs" />
@ -224,7 +235,6 @@
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Helpers\DataTablesModelBinder.cs" />
<Compile Include="Helpers\HtmlIncludeExtentions.cs" />
<Compile Include="Helpers\LinkHelper.cs" />
<Compile Include="Helpers\ProfilerHelper.cs" />
@ -232,10 +242,12 @@
<Compile Include="Helpers\DescriptionExtension.cs" />
<Compile Include="Helpers\HtmlPrefixScopeExtensions.cs" />
<Compile Include="Helpers\IsCurrentActionHelper.cs" />
<Compile Include="Models\DataTablesParams.cs" />
<Compile Include="Models\SearchDetailsModel.cs" />
<Compile Include="Models\JobModel.cs" />
<Compile Include="Models\LogModel.cs" />
<Compile Include="Models\PostUpgradeModel.cs" />
<Compile Include="Models\SearchItemModel.cs" />
<Compile Include="Models\SearchHistoryModel.cs" />
<Compile Include="Models\UpcomingEpisodesModel.cs" />
<Compile Include="Models\SeasonModel.cs" />
<Compile Include="Models\SeriesDetailsModel.cs" />
@ -279,7 +291,6 @@
<Content Include="Content\Images\Indexers\Unknown.png" />
<Content Include="Content\Images\Plus.png" />
<Content Include="Content\Images\VideoFolder.png" />
<Content Include="Content\Images\X.png" />
<Content Include="Content\Settings.css" />
<Content Include="Content\NzbDrone.css" />
<Content Include="Content\Images\XbmcNotification.png" />
@ -518,6 +529,15 @@
<Content Include="Views\Shared\NoSeriesBanner.cshtml" />
<Content Include="Views\Update\Post.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\SearchHistory\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\SearchHistory\Details.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\System\Stats.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
@ -550,4 +570,5 @@
if not exist "$(TargetDir)x86" md "$(TargetDir)x86"
xcopy /s /y "$(SolutionDir)packages\SqlServerCompact.4.0.8482.1\NativeBinaries\x86\*.*" "$(TargetDir)x86"</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
</Project>

Some files were not shown because too many files have changed in this diff Show More