Merge branch 'develop'

pull/2/head
kayone 11 years ago
commit db690adadf

@ -105,69 +105,65 @@ module.exports = function (grunt) {
},
copy: {
index : {
content: {
cwd : srcRoot,
expand: true,
src : '*ndex.html',
src : [
'index.html',
'**/*.css',
'**/*.png',
'**/*.jpg',
'**/*.ico',
'**/FontAwesome/*.*',
'**/fonts/*.*'
],
dest : outputDir
},
scripts: {
cwd : srcRoot,
expand: true,
src : '**/*.js',
dest : outputDir
},
styles : {
cwd : srcRoot,
expand: true,
src : '**/*.css',
dest : outputDir
},
images : {
cwd : srcRoot,
expand: true,
src : '**/*.png',
dest : outputDir
},
jpg : {
cwd : srcRoot,
expand: true,
src : '**/*.jpg',
dest : outputDir
},
icon : {
cwd : srcRoot,
expand: true,
src : '**/*.ico',
dest : outputDir
},
fontAwesome : {
cwd : srcRoot,
expand: true,
src : '**/FontAwesome/*.*',
dest : outputDir
},
fonts : {
cwd : srcRoot,
expand: true,
src : '**/fonts/*.*',
src : [
'**/*.js',
],
dest : outputDir
}
},
jshint: {
options: {
'-W030': false,
'-W064': false,
'-W097': false,
'-W100': false,
'undef': true,
'globals': {
'require': true,
'define': true,
'window': true,
'document': true,
'console': true
}
},
all: [
srcRoot + '**/*.js',
'!**/JsLibraries/*.js'
]
},
requirejs: {
compile:{
options: {
mainConfigFile: "_output/UI/app.js",
mainConfigFile: "src/UI/app.js",
fileExclusionRegExp: /^.*\.(?!js$)[^.]+$/,
preserveLicenseComments: true,
dir: "rjs/",
preserveLicenseComments: false,
dir: outputDir,
optimize: 'none',
removeCombined: true,
inlineText: false,
keepBuildDir : true,
modules: [{
name: 'app',
exclude: ['JsLibraries/jquery']
exclude: ['JsLibraries/jquery', 'templates.js']
}],
}
@ -190,37 +186,21 @@ module.exports = function (grunt) {
files: '<%= handlebars.files.src %>',
tasks: ['handlebars']
},
copyIndex : {
files: '<%= copy.index.cwd %><%= copy.index.src %>',
tasks: ['copy:index']
content : {
files: [
'**/index.html',
'**/*.css',
'**/*.png',
'**/*.jpg',
'**/*.ico',
'**/FontAwesome/*.*',
'**/fonts/*.*'
],
tasks: ['copy:content']
},
copyScripts: {
scripts: {
files: '<%= copy.scripts.cwd %><%= copy.scripts.src %>',
tasks: ['copy:scripts']
},
copyStyles : {
files: '<%= copy.styles.cwd %><%= copy.styles.src %>',
tasks: ['copy:styles']
},
copyImages : {
files: '<%= copy.images.cwd %><%= copy.images.src %>',
tasks: ['copy:images']
},
copyJpg : {
files: '<%= copy.jpg.cwd %><%= copy.jpg.src %>',
tasks: ['copy:jpg']
},
copyIcon : {
files: '<%= copy.icon.cwd %><%= copy.icon.src %>',
tasks: ['copy:icon']
},
copyFontAwesome : {
files: '<%= copy.fontAwesome.cwd %><%= copy.fontAwesome.src %>',
tasks: ['copy:fontAwesome']
},
copyFonts : {
files: '<%= copy.fonts.cwd %><%= copy.fonts.src %>',
tasks: ['copy:fonts']
}
}
});
@ -233,8 +213,10 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-notify');
grunt.loadNpmTasks('grunt-curl');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('package', ['clean:output', 'copy', 'less', 'handlebars']);
grunt.registerTask('package', ['clean:output', 'jshint', 'handlebars', 'copy', 'less']);
grunt.registerTask('packagerjs', ['clean:output','jshint', 'handlebars', 'requirejs', 'copy:content', 'less']);
grunt.registerTask('default', ['package', 'watch']);
grunt.registerTask('update', ['curl']);

@ -0,0 +1 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />

@ -6,6 +6,8 @@ $testSearchPattern = '*.Test\bin\x86\Release'
Function Build()
{
Write-Host "##teamcity[progressStart 'Build']"
$clean = $msbuild + " src\nzbdrone.sln /t:Clean /m"
$build = $msbuild + " src\nzbdrone.sln /p:Configuration=Release /p:Platform=x86 /t:Build /m"
@ -23,6 +25,8 @@ Function Build()
CleanFolder $outputFolder
AddJsonNet
Write-Host "##teamcity[progressFinish 'Build']"
}
Function CleanFolder($path)
@ -53,6 +57,8 @@ Function CleanFolder($path)
Function PackageMono()
{
Write-Host "##teamcity[progressStart 'Creating Mono Package']"
if(Test-Path $outputFolderMono)
{
Remove-Item -Recurse -Force $outputFolderMono -ErrorAction Continue
@ -77,6 +83,8 @@ Function PackageMono()
Write-Host Renaming NzbDrone.Console.exe to NzbDrone.exe
get-childitem $outputFolderMono -File -Filter NzbDrone.exe -Recurse | foreach ($_) {remove-item $_.fullname}
Rename-Item "$outputFolderMono\NzbDrone.Console.exe" "NzbDrone.exe"
Write-Host "##teamcity[progressFinish 'Creating Mono Package']"
}
@ -89,7 +97,9 @@ Function AddJsonNet()
Function PackageTests()
{
Write-Host Packaging Tests
Write-Host "##teamcity[progressStart 'Creating Mono Package']"
if(Test-Path $testPackageFolder)
{
@ -111,17 +121,22 @@ Function PackageTests()
get-childitem $testPackageFolder -File -Filter *log.config | foreach ($_) {remove-item $_.fullname}
CleanFolder $testPackageFolder
Write-Host "##teamcity[progressFinish 'Creating Mono Package']"
}
Function RunGrunt()
{
Write-Host "##teamcity[progressStart 'Running Grunt']"
$gruntPath = [environment]::getfolderpath("applicationdata") + '\npm\node_modules\grunt-cli\bin\grunt'
Invoke-Expression 'npm install'
CheckExitCode
Invoke-Expression ('node ' + $gruntPath + ' package')
CheckExitCode
Write-Host "##teamcity[progressFinish 'Running Grunt']"
}
Function CheckExitCode()

@ -23,6 +23,7 @@
"grunt-curl": "*",
"grunt-notify": "*",
"grunt-contrib-clean": "*",
"grunt-contrib-requirejs": "*"
"grunt-contrib-requirejs": "*",
"grunt-contrib-jshint": "*"
}
}

Binary file not shown.

@ -2,38 +2,62 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
<!-- 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>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
<!-- 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>
<!-- Determines if package restore consent is required to restore packages -->
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
<!-- Download NuGet.exe if it does not already exist -->
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
</PropertyGroup>
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
<!--
<PackageSource Include="https://www.nuget.org/api/v2/" />
<PackageSource Include="https://my-nuget-source/nuget/" />
-->
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
<!-- Windows specific commands -->
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
<PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
<PackagesConfig>packages.config</PackagesConfig>
</PropertyGroup>
<PropertyGroup>
<!-- NuGet command -->
<NuGetExePath>$(NuGetToolsPath)\nuget.exe</NuGetExePath>
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
<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>"http://build.nzbdrone.com/guestAuth/app/nuget/v1/FeedService.svc";"https://nuget.org/api/v2/"</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>
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source $(PackageSources) -o "$(PackagesDir)"</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols</BuildCommand>
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- Make the build depend on restore packages -->
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
@ -48,7 +72,17 @@
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="!Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<!--
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
This effectively acts as a lock that makes sure that the download operation will only happen once and all
parallel builds will have to wait for it to complete.
-->
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
</Target>
<Target Name="_DownloadNuGet">
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
@ -68,4 +102,35 @@
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputFilename ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Net" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
OutputFilename = Path.GetFullPath(OutputFilename);
Log.LogMessage("Downloading latest version of NuGet.exe...");
WebClient webClient = new WebClient();
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
</Project>

@ -17,11 +17,10 @@ namespace NzbDrone.Api.ErrorManagement
_logger = logger;
}
public Response HandleException(NancyContext context, Exception aggregateException)
public Response HandleException(NancyContext context, Exception exception)
{
var innerException = (aggregateException.InnerException).InnerException;
var apiException = innerException as ApiException;
var apiException = exception as ApiException;
if (apiException != null)
{
@ -29,7 +28,7 @@ namespace NzbDrone.Api.ErrorManagement
return apiException.ToErrorResponse();
}
var validationException = innerException as ValidationException;
var validationException = exception as ValidationException;
if (validationException != null)
{
@ -38,23 +37,23 @@ namespace NzbDrone.Api.ErrorManagement
return validationException.Errors.AsResponse(HttpStatusCode.BadRequest);
}
var clientException = innerException as NzbDroneClientException;
var clientException = exception as NzbDroneClientException;
if (clientException != null)
{
return new ErrorModel
{
Message = innerException.Message,
Description = innerException.ToString()
Message = exception.Message,
Description = exception.ToString()
}.AsResponse((HttpStatusCode)clientException.StatusCode);
}
_logger.FatalException("Request Failed", innerException);
_logger.FatalException("Request Failed", exception);
return new ErrorModel
{
Message = innerException.Message,
Description = innerException.ToString()
Message = exception.Message,
Description = exception.ToString()
}.AsResponse(HttpStatusCode.InternalServerError);
}
}

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;
namespace NzbDrone.Api.Extensions

@ -19,7 +19,6 @@ namespace NzbDrone.Api.Mapping
source.GetType().GetGenericTypeDefinition() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
{
var result = new TTarget();
var listSubType = targetType.GetGenericArguments()[0];

@ -38,29 +38,29 @@
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="FluentValidation, Version=4.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="FluentValidation, Version=5.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentValidation.4.0.0.1\lib\Net40\FluentValidation.dll</HintPath>
<HintPath>..\packages\FluentValidation.5.0.0.1\lib\Net40\FluentValidation.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.SignalR.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.1.1.3\lib\net40\Microsoft.AspNet.SignalR.Core.dll</HintPath>
</Reference>
<Reference Include="Nancy, Version=0.20.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Nancy, Version=0.21.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Nancy.0.20.0\lib\net40\Nancy.dll</HintPath>
<HintPath>..\packages\Nancy.0.21.1\lib\net40\Nancy.dll</HintPath>
</Reference>
<Reference Include="Nancy.Authentication.Basic, Version=0.20.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Nancy.Authentication.Basic, Version=0.21.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Nancy.Authentication.Basic.0.20.0\lib\net40\Nancy.Authentication.Basic.dll</HintPath>
<HintPath>..\packages\Nancy.Authentication.Basic.0.21.1\lib\net40\Nancy.Authentication.Basic.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="Omu.ValueInjecter">
<HintPath>..\packages\ValueInjecter.2.3.3\lib\net35\Omu.ValueInjecter.dll</HintPath>

@ -1,15 +1,17 @@
using System.Collections.Generic;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.RootFolders;
using NzbDrone.Api.Mapping;
using NzbDrone.Api.Validation;
namespace NzbDrone.Api.RootFolders
{
public class RootFolderModule : NzbDroneRestModule<RootFolderResource>
public class RootFolderModule : NzbDroneRestModuleWithSignalR<RootFolderResource, RootFolder>
{
private readonly IRootFolderService _rootFolderService;
public RootFolderModule(IRootFolderService rootFolderService)
public RootFolderModule(IRootFolderService rootFolderService, ICommandExecutor commandExecutor)
: base(commandExecutor)
{
_rootFolderService = rootFolderService;
@ -18,7 +20,7 @@ namespace NzbDrone.Api.RootFolders
CreateResource = CreateRootFolder;
DeleteResource = DeleteFolder;
SharedValidator.RuleFor(c=>c.Path).IsValidPath();
SharedValidator.RuleFor(c => c.Path).IsValidPath();
}
private RootFolderResource GetRootFolder(int id)

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentValidation" version="4.0.0.1" targetFramework="net40" />
<package id="FluentValidation" version="5.0.0.1" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.Core" version="1.1.3" targetFramework="net40" />
<package id="Nancy" version="0.20.0" targetFramework="net40" />
<package id="Nancy.Authentication.Basic" version="0.20.0" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="Nancy" version="0.21.1" targetFramework="net40" />
<package id="Nancy.Authentication.Basic" version="0.21.1" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="ValueInjecter" version="2.3.3" targetFramework="net40" />
</packages>

@ -44,9 +44,9 @@
<Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.6.2.12296, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>

@ -3,6 +3,6 @@
<package id="FluentAssertions" version="2.1.0.0" targetFramework="net40" />
<package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1.1" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
</packages>

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using FluentAssertions;
using NLog;
using NLog.Config;
using NLog.Targets;
using NUnit.Framework;
using NzbDrone.Automation.Test.PageModel;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Test.Common;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.UI;
namespace NzbDrone.Automation.Test
{
[TestFixture]
[AutomationTest]
public abstract class AutomationTest
{
private NzbDroneRunner _runner;
protected RemoteWebDriver driver;
public AutomationTest()
{
new StartupArguments();
LogManager.Configuration = new LoggingConfiguration();
var consoleTarget = new ConsoleTarget { Layout = "${level}: ${message} ${exception}" };
LogManager.Configuration.AddTarget(consoleTarget.GetType().Name, consoleTarget);
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, consoleTarget));
}
[TestFixtureSetUp]
public void SmokeTestSetup()
{
driver = new FirefoxDriver();
_runner = new NzbDroneRunner();
_runner.KillAll();
_runner.Start();
driver.Url = "http://localhost:8989";
var page = new PageBase(driver);
page.WaitForNoSpinner();
GetPageErrors().Should().BeEmpty();
}
protected IEnumerable<string> GetPageErrors()
{
return driver.FindElements(By.CssSelector("#errors div"))
.Select(e => e.Text);
}
[TestFixtureTearDown]
public void SmokeTestTearDown()
{
_runner.KillAll();
driver.Quit();
}
[TearDown]
public void AutomationTearDown()
{
GetPageErrors().Should().BeEmpty();
}
}
}

@ -0,0 +1,13 @@
using NUnit.Framework;
namespace NzbDrone.Automation.Test
{
public class AutomationTestAttribute : CategoryAttribute
{
public AutomationTestAttribute()
: base("AutomationTest")
{
}
}
}

@ -0,0 +1,81 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Automation.Test.PageModel;
using OpenQA.Selenium;
namespace NzbDrone.Automation.Test
{
[TestFixture]
public class MainPagesTest : AutomationTest
{
private PageBase page;
[SetUp]
public void Setup()
{
page = new PageBase(driver);
}
[Test]
public void series_page()
{
page.SeriesNavIcon.Click();
page.WaitForNoSpinner();
page.FindByClass("iv-series-index-seriesindexlayout").Should().NotBeNull();
}
[Test]
public void calendar_page()
{
page.CalendarNavIcon.Click();
page.WaitForNoSpinner();
page.FindByClass("iv-calendar-calendarlayout").Should().NotBeNull();
}
[Test]
public void history_page()
{
page.HistoryNavIcon.Click();
page.WaitForNoSpinner();
page.FindByClass("iv-history-historylayout").Should().NotBeNull();
}
[Test]
public void missing_page()
{
page.MissingNavIcon.Click();
page.WaitForNoSpinner();
page.FindByClass("iv-missing-missinglayout").Should().NotBeNull();
}
[Test]
public void system_page()
{
page.SystemNavIcon.Click();
page.WaitForNoSpinner();
page.FindByClass("iv-system-systemlayout").Should().NotBeNull();
}
[Test]
public void add_series_page()
{
page.SeriesNavIcon.Click();
page.WaitForNoSpinner();
page.Find(By.LinkText("Add Series")).Click();
page.WaitForNoSpinner();
page.FindByClass("iv-addseries-addserieslayout").Should().NotBeNull();
}
}
}

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CC26800D-F67E-464B-88DE-8EB1A0C227A3}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NzbDrone.Automation.Test</RootNamespace>
<AssemblyName>NzbDrone.Automation.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|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="FluentAssertions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll</HintPath>
</Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.2\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="WebDriver">
<HintPath>..\packages\Selenium.WebDriver.2.37.0\lib\net40\WebDriver.dll</HintPath>
</Reference>
<Reference Include="WebDriver.Support">
<HintPath>..\packages\Selenium.Support.2.37.0\lib\net40\WebDriver.Support.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AutomationTestAttribute.cs" />
<Compile Include="AutomationTest.cs" />
<Compile Include="MainPagesTest.cs" />
<Compile Include="PageModel\PageBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Common\NzbDrone.Common.csproj">
<Project>{F2BE0FDF-6E47-4827-A420-DD4EF82407F8}</Project>
<Name>NzbDrone.Common</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.Test.Common\NzbDrone.Test.Common.csproj">
<Project>{CADDFCE0-7509-4430-8364-2074E1EEFCA2}</Project>
<Name>NzbDrone.Test.Common</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(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">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

@ -0,0 +1,100 @@
using System;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.UI;
namespace NzbDrone.Automation.Test.PageModel
{
public class PageBase
{
private readonly RemoteWebDriver _driver;
public PageBase(RemoteWebDriver driver)
{
_driver = driver;
}
public IWebElement FindByClass(string className, int timeout = 5)
{
return Find(By.ClassName(className), timeout);
}
public IWebElement Find(By by, int timeout = 5)
{
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(timeout));
return wait.Until(d => d.FindElement(by));
}
public void WaitForNoSpinner(int timeout = 20)
{
//give the spinner some time to show up.
Thread.Sleep(100);
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(timeout));
wait.Until(d =>
{
try
{
IWebElement element = d.FindElement(By.Id("followingBalls"));
return !element.Displayed;
}
catch (NoSuchElementException)
{
return true;
}
});
}
public IWebElement SeriesNavIcon
{
get
{
return Find(By.LinkText("Series"));
}
}
public IWebElement CalendarNavIcon
{
get
{
return Find(By.LinkText("Calendar"));
}
}
public IWebElement HistoryNavIcon
{
get
{
return Find(By.LinkText("History"));
}
}
public IWebElement MissingNavIcon
{
get
{
return Find(By.LinkText("Missing"));
}
}
public IWebElement SettingNavIcon
{
get
{
return Find(By.LinkText("Settings"));
}
}
public IWebElement SystemNavIcon
{
get
{
return Find(By.LinkText("System"));
}
}
}
}

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("NzbDrone.Automation.Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NzbDrone.Automation.Test")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("6b8945f5-f5b5-4729-865d-f958fbd673d9")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="2.1.0.0" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
<package id="Selenium.Support" version="2.37.0" targetFramework="net40" />
<package id="Selenium.WebDriver" version="2.37.0" targetFramework="net40" />
</packages>

@ -155,10 +155,14 @@ namespace NzbDrone.Common.Test.DiskProviderTests
[Test]
public void folder_should_return_correct_value_for_last_write()
{
var testFile = GetTestFilePath();
var testDir = Path.Combine(SandboxFolder, "LastWrite");
var testFile = Path.Combine(testDir, Path.GetRandomFileName());
Directory.CreateDirectory(testDir);
TestLogger.Info("Path is: {0}", testFile);
Subject.WriteAllText(testFile, "Test");
Subject.GetLastFolderWrite(SandboxFolder).Should().BeOnOrAfter(DateTime.UtcNow.AddMinutes(-1));

@ -41,9 +41,9 @@
<Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.6.2.12296, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>

@ -2,6 +2,6 @@
<packages>
<package id="FluentAssertions" version="2.1.0.0" targetFramework="net40" />
<package id="Moq" version="4.0.10827" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
</packages>

@ -301,27 +301,18 @@ namespace NzbDrone.Common
if (OsInfo.IsLinux)
{
var drives = DriveInfo.GetDrives();
foreach (var drive in drives)
try
{
try
{
if (drive.IsReady && path.StartsWith(drive.Name, StringComparison.CurrentCultureIgnoreCase))
{
return drive.AvailableFreeSpace;
}
}
catch (InvalidOperationException e)
{
Logger.ErrorException("Couldn't get free space for " + path, e);
}
return GetDriveInfoLinux(path).AvailableFreeSpace;
}
catch (InvalidOperationException e)
{
Logger.ErrorException("Couldn't get free space for " + path, e);
}
return null;
}
return DriveFreeSpaceEx(root);
}
@ -471,27 +462,18 @@ namespace NzbDrone.Common
if (OsInfo.IsLinux)
{
var drives = DriveInfo.GetDrives();
foreach (var drive in drives)
try
{
try
{
if (drive.IsReady && path.StartsWith(drive.Name, StringComparison.CurrentCultureIgnoreCase))
{
return drive.TotalSize;
}
}
catch (InvalidOperationException e)
{
Logger.ErrorException("Couldn't get total space for " + path, e);
}
return GetDriveInfoLinux(path).TotalSize;
}
catch (InvalidOperationException e)
{
Logger.ErrorException("Couldn't get total space for " + path, e);
}
return null;
}
return DriveTotalSizeEx(root);
}
@ -554,5 +536,16 @@ namespace NzbDrone.Common
return 0;
}
private DriveInfo GetDriveInfoLinux(string path)
{
var drives = DriveInfo.GetDrives();
return
drives.Where(drive =>
drive.IsReady && path.StartsWith(drive.Name, StringComparison.CurrentCultureIgnoreCase))
.OrderByDescending(drive => drive.Name.Length)
.First();
}
}
}

@ -92,7 +92,6 @@ namespace NzbDrone.Common.EnsureThat
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid relative path. relative paths can not start with \\", param.Value));
}
return param;
}

@ -46,11 +46,11 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="loggly-csharp" version="2.3" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net40" />
</packages>

@ -81,11 +81,11 @@
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>

@ -4,7 +4,7 @@
<package id="Microsoft.AspNet.SignalR.Owin" version="1.1.3" targetFramework="net40" />
<package id="Microsoft.Owin" version="1.1.0-beta2" targetFramework="net40" />
<package id="Microsoft.Owin.Hosting" version="1.1.0-beta2" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="Owin" version="1.0" targetFramework="net40" />
</packages>
</packages>

@ -0,0 +1,37 @@
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class CleanupAdditionalNamingSpecsFixture : DbTest<CleanupAdditionalNamingSpecs, NamingConfig>
{
[Test]
public void should_delete_additional_naming_configs()
{
var specs = Builder<NamingConfig>.CreateListOfSize(5)
.BuildListOfNew();
Db.InsertMany(specs);
Subject.Clean();
AllStoredModels.Should().HaveCount(1);
}
[Test]
public void should_not_delete_if_only_one_spec()
{
var spec = Builder<NamingConfig>.CreateNew()
.BuildNew();
Db.Insert(spec);
Subject.Clean();
AllStoredModels.Should().HaveCount(1);
}
}
}

@ -124,7 +124,7 @@ namespace NzbDrone.Core.Test.MediaFiles
imported.Add(new ImportDecision(localEpisode));
Mocker.GetMock<IMakeImportDecision>()
.Setup(s => s.GetImportDecisions(It.IsAny<IEnumerable<String>>(), It.IsAny<Series>(), true))
.Setup(s => s.GetImportDecisions(It.IsAny<IEnumerable<String>>(), It.IsAny<Series>(), true, null))
.Returns(imported);
Mocker.GetMock<IImportApprovedEpisodes>()

@ -9,6 +9,7 @@ using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
@ -21,6 +22,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
private List<string> _videoFiles;
private LocalEpisode _localEpisode;
private Series _series;
private QualityModel _quality;
private Mock<IImportDecisionEngineSpecification> _pass1;
private Mock<IImportDecisionEngineSpecification> _pass2;
@ -62,7 +64,13 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_videoFiles = new List<string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" };
_series = new Series();
_localEpisode = new LocalEpisode { Series = _series, Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" };
_quality = new QualityModel(Quality.DVD);
_localEpisode = new LocalEpisode
{
Series = _series,
Quality = _quality,
Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi"
};
Mocker.GetMock<IParsingService>()
.Setup(c => c.GetEpisodes(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<Boolean>()))
@ -161,5 +169,38 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
ExceptionVerification.ExpectedErrors(3);
}
[Test]
public void should_use_file_quality_if_folder_quality_is_null()
{
GivenSpecifications(_pass1, _pass2, _pass3);
var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single());
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false, null);
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
}
[Test]
public void should_use_file_quality_if_folder_quality_is_lower_than_file_quality()
{
GivenSpecifications(_pass1, _pass2, _pass3);
var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single());
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false, new QualityModel(Quality.SDTV));
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
}
[Test]
public void should_use_folder_quality_when_it_is_greater_than_file_quality()
{
GivenSpecifications(_pass1, _pass2, _pass3);
var expectedQuality = new QualityModel(Quality.Bluray1080p);
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false, expectedQuality);
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
}
}
}

@ -46,13 +46,14 @@ namespace NzbDrone.Core.Test.MediaFiles
{
Series = series,
Episodes = new List<Episode> {episode},
Path = @"C:\Test\TV\30 Rock\30 Rock - S01E01 - Pilit.avi".AsOsAgnostic(),
Path = @"C:\Test\TV\30 Rock\30 Rock - S01E01 - Pilot.avi".AsOsAgnostic(),
Quality = new QualityModel(Quality.Bluray720p)
}));
}
Mocker.GetMock<IUpgradeMediaFiles>()
.Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>()));
.Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>()))
.Returns(new EpisodeFileMoveResult());
}
[Test]

@ -1,5 +1,6 @@
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Marr.Data;
using Moq;
using NUnit.Framework;
@ -149,5 +150,21 @@ namespace NzbDrone.Core.Test.MediaFiles
Mocker.GetMock<IRecycleBinProvider>().Verify(v => v.DeleteFile(It.IsAny<string>()), Times.Never());
}
[Test]
public void should_return_old_episode_file_in_oldFiles()
{
GivenSingleEpisodeWithSingleEpisodeFile();
Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode).OldFiles.Count.Should().Be(1);
}
[Test]
public void should_return_old_episode_files_in_oldFiles()
{
GivenMultipleEpisodesWithMultipleEpisodeFiles();
Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode).OldFiles.Count.Should().Be(2);
}
}
}

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.NotificationTests.Xbmc
{
[TestFixture]
public class OnDownloadFixture : CoreTest<Notifications.Xbmc.Xbmc>
{
private DownloadMessage _downloadMessage;
[SetUp]
public void Setup()
{
var series = Builder<Series>.CreateNew()
.Build();
var episodeFile = Builder<EpisodeFile>.CreateNew()
.Build();
_downloadMessage = Builder<DownloadMessage>.CreateNew()
.With(d => d.Series = series)
.With(d => d.EpisodeFile = episodeFile)
.With(d => d.OldFiles = new List<EpisodeFile>())
.Build();
Subject.Definition = new NotificationDefinition();
Subject.Definition.Settings = new XbmcSettings
{
UpdateLibrary = true
};
}
private void GivenOldFiles()
{
_downloadMessage.OldFiles = Builder<EpisodeFile>.CreateListOfSize(1)
.Build()
.ToList();
Subject.Definition.Settings = new XbmcSettings
{
UpdateLibrary = true,
CleanLibrary = true
};
}
[Test]
public void should_not_clean_if_no_episode_was_replaced()
{
Subject.OnDownload(_downloadMessage);
Mocker.GetMock<IXbmcService>().Verify(v => v.Clean(It.IsAny<XbmcSettings>()), Times.Never());
}
[Test]
public void should_clean_if_episode_was_replaced()
{
GivenOldFiles();
Subject.OnDownload(_downloadMessage);
Mocker.GetMock<IXbmcService>().Verify(v => v.Clean(It.IsAny<XbmcSettings>()), Times.Once());
}
}
}

@ -55,8 +55,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentMigrator.1.1.1.0\tools\FluentMigrator.Runner.dll</HintPath>
</Reference>
<Reference Include="FluentValidation">
<HintPath>..\packages\FluentValidation.4.0.0.1\lib\Net40\FluentValidation.dll</HintPath>
<Reference Include="FluentValidation, Version=5.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentValidation.5.0.0.1\lib\Net40\FluentValidation.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Practices.ServiceLocation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@ -79,11 +80,11 @@
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.6.2.12296, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@ -131,6 +132,7 @@
<Compile Include="Framework\NBuilderExtensions.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedHistoryItemsFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedEpisodeFilesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupAdditionalNamingSpecsFixture.cs" />
<Compile Include="IndexerSearchTests\SearchDefinitionFixture.cs" />
<Compile Include="IndexerTests\BasicRssParserFixture.cs" />
<Compile Include="IndexerTests\IndexerServiceFixture.cs" />
@ -167,6 +169,7 @@
<Compile Include="NotificationTests\Xbmc\Json\CheckForErrorFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Json\GetSeriesPathFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Json\UpdateFixture.cs" />
<Compile Include="NotificationTests\Xbmc\OnDownloadFixture.cs" />
<Compile Include="OrganizerTests\BuildFilePathFixture.cs" />
<Compile Include="ParserTests\ParsingServiceTests\GetEpisodesFixture.cs" />
<Compile Include="ParserTests\ParsingServiceTests\GetSeriesFixture.cs" />

@ -120,7 +120,7 @@ namespace NzbDrone.Core.Test.ParserTests
ExceptionVerification.IgnoreWarns();
}
[TestCase("[DmonHiro] The Severing Crime Edge - Cut 02 - Portrait Of Heresy [BD, 720p] [BE36E9E0]")]
[TestCase("THIS SHOULD NEVER PARSE")]
public void unparsable_title_should_log_warn_and_return_null(string title)
{
Parser.Parser.ParseTitle(title).Should().BeNull();
@ -147,7 +147,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Hell.on.Wheels.S02E09-E10.720p.HDTV.x264-EVOLVE", "Hell on Wheels", 2, new[] { 9, 10 })]
[TestCase("Grey's Anatomy - 8x01_02 - Free Falling", "Grey's Anatomy", 8, new [] { 1,2 })]
[TestCase("8x01_02 - Free Falling", "", 8, new[] { 1, 2 })]
[TestCase("Kaamelott.S01E91-E100", "Kaamelott", 1,new[] { 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 })]
[TestCase("Kaamelott.S01E91-E100", "Kaamelott", 1, new[] { 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 })]
[TestCase("Neighbours.S29E161-E165.PDTV.x264-FQM", "Neighbours", 29, new[] { 161, 162, 163, 164, 165 })]
[TestCase("Shortland.Street.S22E5363-E5366.HDTV.x264-FiHTV", "Shortland Street", 22, new[] { 5363, 5364, 5365, 5366 })]
public void TitleParse_multi(string postTitle, string title, int season, int[] episodes)
{
var result = Parser.Parser.ParseTitle(postTitle);
@ -174,7 +176,39 @@ namespace NzbDrone.Core.Test.ParserTests
result.Should().NotBeNull();
result.SeriesTitle.Should().Be(title.CleanSeriesTitle());
result.AirDate.Should().Be(airDate.ToString(Episode.AIR_DATE_FORMAT));
result.EpisodeNumbers.Should().BeNull();
result.EpisodeNumbers.Should().BeEmpty();
}
[TestCase("[SubDESU]_High_School_DxD_07_(1280x720_x264-AAC)_[6B7FD717]", "High School DxD", 7, 0, 0)]
[TestCase("[Chihiro]_Working!!_-_06_[848x480_H.264_AAC][859EEAFA]", "Working!!", 6, 0, 0)]
[TestCase("[Commie]_Senki_Zesshou_Symphogear_-_11_[65F220B4]", "Senki_Zesshou_Symphogear", 11, 0, 0)]
[TestCase("[Underwater]_Rinne_no_Lagrange_-_12_(720p)_[5C7BC4F9]", "Rinne_no_Lagrange", 12, 0, 0)]
[TestCase("[Commie]_Rinne_no_Lagrange_-_15_[E76552EA]", "Rinne_no_Lagrange", 15, 0, 0)]
[TestCase("[HorribleSubs]_Hunter_X_Hunter_-_33_[720p]", "Hunter_X_Hunter", 33, 0, 0)]
[TestCase("[HorribleSubs]_Fairy_Tail_-_145_[720p]", "Fairy_Tail", 145, 0, 0)]
[TestCase("[HorribleSubs] Tonari no Kaibutsu-kun - 13 [1080p].mkv", "Tonari no Kaibutsu-kun", 13, 0, 0)]
[TestCase("[Doremi].Yes.Pretty.Cure.5.Go.Go!.31.[1280x720].[C65D4B1F].mkv", "Yes.Pretty.Cure.5.Go.Go!", 31, 0, 0)]
[TestCase("[K-F] One Piece 214", "One Piece", 214, 0, 0)]
[TestCase("[K-F] One Piece S10E14 214", "One Piece", 214, 10, 14)]
[TestCase("[K-F] One Piece 10x14 214", "One Piece", 214, 10, 14)]
[TestCase("[K-F] One Piece 214 10x14", "One Piece", 214, 10, 14)]
[TestCase("One Piece S10E14 214", "One Piece", 214, 10, 14)]
[TestCase("One Piece 10x14 214", "One Piece", 214, 10, 14)]
[TestCase("One Piece 214 10x14", "One Piece", 214, 10, 14)]
[TestCase("214 One Piece 10x14", "One Piece", 214, 10, 14)]
[TestCase("Bleach - 031 - The Resolution to Kill [Lunar].avi", "Bleach", 31, 0, 0)]
[TestCase("Bleach - 031 - The Resolution to Kill [Lunar]", "Bleach", 31, 0, 0)]
[TestCase("[ACX]Hack Sign 01 Role Play [Kosaka] [9C57891E].mkv", "Hack Sign", 1, 0, 0)]
[TestCase("[SFW-sage] Bakuman S3 - 12 [720p][D07C91FC]", "Bakuman S3", 12, 0, 0)]
[TestCase("ducktales_e66_time_is_money_part_one_marking_time", "DuckTales", 66, 0, 0)]
public void parse_absolute_numbers(string postTitle, string title, int absoluteEpisodeNumber, int seasonNumber, int episodeNumber)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Should().NotBeNull();
result.AbsoluteEpisodeNumbers.First().Should().Be(absoluteEpisodeNumber);
result.SeasonNumber.Should().Be(seasonNumber);
result.EpisodeNumbers.FirstOrDefault().Should().Be(episodeNumber);
result.SeriesTitle.Should().Be(title.CleanSeriesTitle());
}

@ -38,7 +38,8 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
{
SeriesTitle = _series.Title,
SeasonNumber = 1,
EpisodeNumbers = new[] { 1 }
EpisodeNumbers = new[] { 1 },
AbsoluteEpisodeNumbers = new int[0]
};
_singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria
@ -69,6 +70,11 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
_series.UseSceneNumbering = true;
}
private void GivenAbsoluteNumberingSeries()
{
_parsedEpisodeInfo.AbsoluteEpisodeNumbers = new[] { 1 };
}
[Test]
public void should_get_daily_episode_episode_when_search_criteria_is_null()
{
@ -105,6 +111,17 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
.Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<String>()), Times.Once());
}
[Test]
public void should_use_search_criteria_episode_when_it_matches_absolute()
{
GivenAbsoluteNumberingSeries();
Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<String>()), Times.Never());
}
[Test]
public void should_use_scene_numbering_when_series_uses_scene_numbering()
{

@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
[Test]
public void should_return_one_drive_when_only_one_root_dir_exists()
{
Mocker.GetMock<IBasicRepository<RootFolder>>()
Mocker.GetMock<IRootFolderRepository>()
.Setup(s => s.All())
.Returns(new List<RootFolder> { new RootFolder { Id = 1, Path = @"C:\Test\TV" } });
@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
[Test]
public void should_return_one_drive_when_two_rootDirs_on_the_same_drive_exist()
{
Mocker.GetMock<IBasicRepository<RootFolder>>()
Mocker.GetMock<IRootFolderRepository>()
.Setup(s => s.All())
.Returns(new List<RootFolder> { new RootFolder { Id = 1, Path = @"C:\Test\TV" },
new RootFolder { Id = 2, Path = @"C:\Test\TV2" }});
@ -62,7 +62,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
[Test]
public void should_return_two_drives_when_two_rootDirs_on_the_different_drive_exist()
{
Mocker.GetMock<IBasicRepository<RootFolder>>()
Mocker.GetMock<IRootFolderRepository>()
.Setup(s => s.All())
.Returns(new List<RootFolder> { new RootFolder { Id = 1, Path = @"C:\Test\TV" },
new RootFolder { Id = 2, Path = @"D:\Test\TV" }});
@ -87,7 +87,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
[Test]
public void should_skip_rootDir_if_not_found_on_disk()
{
Mocker.GetMock<IBasicRepository<RootFolder>>()
Mocker.GetMock<IRootFolderRepository>()
.Setup(s => s.All())
.Returns(new List<RootFolder> { new RootFolder { Id = 1, Path = @"C:\Test\TV" } });

@ -1,13 +1,10 @@

using System;
using System;
using System.Collections.Generic;
using System.IO;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
@ -25,7 +22,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
.Setup(m => m.FolderExists(It.IsAny<string>()))
.Returns(true);
Mocker.GetMock<IBasicRepository<RootFolder>>()
Mocker.GetMock<IRootFolderRepository>()
.Setup(s => s.All())
.Returns(new List<RootFolder>());
}
@ -45,7 +42,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
Subject.Add(root);
Mocker.GetMock<IBasicRepository<RootFolder>>().Verify(c => c.Insert(root), Times.Once());
Mocker.GetMock<IRootFolderRepository>().Verify(c => c.Insert(root), Times.Once());
}
[Test]
@ -60,14 +57,15 @@ namespace NzbDrone.Core.Test.RootFolderTests
public void should_be_able_to_remove_root_dir()
{
Subject.Remove(1);
Mocker.GetMock<IBasicRepository<RootFolder>>().Verify(c => c.Delete(1), Times.Once());
Mocker.GetMock<IRootFolderRepository>().Verify(c => c.Delete(1), Times.Once());
}
[Test]
public void None_existing_folder_returns_empty_list()
{
WithNoneExistingFolder();
Mocker.GetMock<IBasicRepository<RootFolder>>().Setup(c => c.All()).Returns(new List<RootFolder>());
Mocker.GetMock<IRootFolderRepository>().Setup(c => c.All()).Returns(new List<RootFolder>());
const string path = "d:\\bad folder";
@ -97,7 +95,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
[Test]
public void adding_duplicated_root_folder_should_throw()
{
Mocker.GetMock<IBasicRepository<RootFolder>>().Setup(c => c.All()).Returns(new List<RootFolder> { new RootFolder { Path = "C:\\TV".AsOsAgnostic() } });
Mocker.GetMock<IRootFolderRepository>().Setup(c => c.All()).Returns(new List<RootFolder> { new RootFolder { Path = "C:\\TV".AsOsAgnostic() } });
Assert.Throws<InvalidOperationException>(() => Subject.Add(new RootFolder { Path = @"C:\TV".AsOsAgnostic() }));
}

@ -20,6 +20,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
.With(e => e.SeasonNumber = 1)
.With(e => e.SceneSeasonNumber = 2)
.With(e => e.EpisodeNumber = 3)
.With(e => e.AbsoluteEpisodeNumber = 3)
.With(e => e.SceneEpisodeNumber = 4)
.Build();
@ -51,5 +52,14 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
.Should()
.BeNull();
}
[Test]
public void should_find_episode_by_absolute_numbering()
{
Subject.Find(_episode.SeriesId, _episode.AbsoluteEpisodeNumber.Value)
.Id
.Should()
.Be(_episode.Id);
}
}
}

@ -4,12 +4,12 @@
<package id="CommonServiceLocator" version="1.0" targetFramework="net40" />
<package id="FluentAssertions" version="2.1.0.0" targetFramework="net40" />
<package id="FluentMigrator" version="1.1.1.0" targetFramework="net40" />
<package id="FluentValidation" version="4.0.0.1" targetFramework="net40" />
<package id="FluentValidation" version="5.0.0.1" targetFramework="net40" />
<package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1.1" />
<package id="NCrunch.Framework" version="1.46.0.9" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
<package id="Prowlin" version="0.9.4456.26422" targetFramework="net40" />
<package id="Unity" version="2.1.505.2" targetFramework="net40" />

@ -116,6 +116,8 @@ namespace NzbDrone.Core.Datastore
DataMapper.Insert(model);
ModelCreated(model);
return model;
}
@ -127,12 +129,15 @@ namespace NzbDrone.Core.Datastore
}
DataMapper.Update(model, c => c.Id == model.Id);
ModelUpdated(model);
return model;
}
public void Delete(TModel model)
{
DataMapper.Delete<TModel>(c => c.Id == model.Id);
Delete(model.Id);
}
public void InsertMany(IList<TModel> models)
@ -199,6 +204,8 @@ namespace NzbDrone.Core.Datastore
.ColumnsIncluding(properties)
.Entity(model)
.Execute();
ModelUpdated(model);
}
public virtual PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec)

@ -1,5 +1,6 @@
using System;
using System.IO;
using NLog;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Configuration;
using RestSharp;
@ -16,10 +17,12 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
public class SabCommunicationProxy : ISabCommunicationProxy
{
private readonly IConfigService _configService;
private readonly Logger _logger;
public SabCommunicationProxy(IConfigService configService)
public SabCommunicationProxy(IConfigService configService, Logger logger)
{
_configService = configService;
_logger = logger;
}
public string DownloadNzb(Stream nzb, string title, string category, int priority)
@ -44,7 +47,8 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{
var client = BuildClient(action);
var response = client.Execute(restRequest);
_logger.Trace("Response: {0}", response);
CheckForError(response);
return response.Content;

@ -0,0 +1,29 @@
using NLog;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class CleanupAdditionalNamingSpecs : IHousekeepingTask
{
private readonly IDatabase _database;
private readonly Logger _logger;
public CleanupAdditionalNamingSpecs(IDatabase database, Logger logger)
{
_database = database;
_logger = logger;
}
public void Clean()
{
_logger.Trace("Running naming spec cleanup");
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM NamingConfig
WHERE ID NOT IN (
SELECT ID FROM NamingConfig
LIMIT 1)");
}
}
}

@ -1,6 +1,5 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{

@ -1,6 +1,5 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.History;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{

@ -101,10 +101,10 @@ namespace NzbDrone.Core.Indexers.Newznab
{
if (tvRageId > 0)
{
return RecentFeed.Select(url => String.Format("{0}&limit=100&rid={1}&season={2:yyyy}&ep={2:MM/dd}", url, tvRageId, date)).ToList();
return RecentFeed.Select(url => String.Format("{0}&limit=100&rid={1}&season={2:yyyy}&ep={2:MM}/{2:dd}", url, tvRageId, date)).ToList();
}
return RecentFeed.Select(url => String.Format("{0}&limit=100&q={1}&season={2:yyyy}&ep={2:MM/dd}", url, NewsnabifyTitle(seriesTitle), date)).ToList();
return RecentFeed.Select(url => String.Format("{0}&limit=100&q={1}&season={2:yyyy}&ep={2:MM}/{2:dd}", url, NewsnabifyTitle(seriesTitle), date)).ToList();
}
public override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int offset)

@ -54,6 +54,7 @@ namespace NzbDrone.Core.MediaFiles
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, series, false);
_importApprovedEpisodes.Import(decisions);
_logger.Info("Completed scanning disk for {0}", series.Title);
}
public string[] GetVideoFiles(string path, bool allDirectories = true)
@ -65,7 +66,7 @@ namespace NzbDrone.Core.MediaFiles
var mediaFileList = filesOnDisk.Where(c => MediaFileExtensions.Extensions.Contains(Path.GetExtension(c).ToLower())).ToList();
_logger.Trace("{0} video files were found in {1}", mediaFileList.Count, path);
_logger.Debug("{0} video files were found in {1}", mediaFileList.Count, path);
return mediaFileList.ToArray();
}

@ -99,6 +99,7 @@ namespace NzbDrone.Core.MediaFiles
{
var cleanedUpName = GetCleanedUpFolderName(subfolderInfo.Name);
var series = _parsingService.GetSeries(cleanedUpName);
var quality = QualityParser.ParseQuality(cleanedUpName);
if (series == null)
{
@ -108,7 +109,7 @@ namespace NzbDrone.Core.MediaFiles
var videoFiles = _diskScanService.GetVideoFiles(subfolderInfo.FullName);
return ProcessFiles(series, videoFiles);
return ProcessFiles(series, quality, videoFiles);
}
private void ProcessVideoFile(string videoFile)
@ -127,12 +128,12 @@ namespace NzbDrone.Core.MediaFiles
return;
}
ProcessFiles(series, videoFile);
ProcessFiles(series, null, videoFile);
}
private List<ImportDecision> ProcessFiles(Series series, params string[] videoFiles)
private List<ImportDecision> ProcessFiles(Series series, QualityModel quality, params string[] videoFiles)
{
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles, series, true);
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles, series, true, quality);
return _importApprovedEpisodes.Import(decisions, true);
}

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.MediaFiles
{
public class EpisodeFileMoveResult
{
public EpisodeFileMoveResult()
{
OldFiles = new List<EpisodeFile>();
}
public String Path { get; set; }
public List<EpisodeFile> OldFiles { get; set; }
}
}

@ -44,6 +44,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
foreach (var importDecision in qualifiedImports.OrderByDescending(e => e.LocalEpisode.Size))
{
var localEpisode = importDecision.LocalEpisode;
var oldFiles = new List<EpisodeFile>();
try
{
@ -65,11 +66,12 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
episodeFile.SeasonNumber = localEpisode.SeasonNumber;
episodeFile.Episodes = localEpisode.Episodes;
if (newDownload)
{
episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
episodeFile.Path = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode);
var moveResult = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode);
episodeFile.Path = moveResult.Path;
oldFiles = moveResult.OldFiles;
}
_mediaFileService.Add(episodeFile);
@ -78,7 +80,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
if (newDownload)
{
_eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile));
_eventAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode));
_eventAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode, episodeFile, oldFiles));
}
}
catch (Exception e)

@ -6,6 +6,7 @@ using NzbDrone.Common;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
@ -13,7 +14,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
{
public interface IMakeImportDecision
{
List<ImportDecision> GetImportDecisions(IEnumerable<String> videoFiles, Series series, bool sceneSource);
List<ImportDecision> GetImportDecisions(IEnumerable<String> videoFiles, Series series, bool sceneSource, QualityModel quality = null);
}
public class ImportDecisionMaker : IMakeImportDecision
@ -38,16 +39,16 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
_logger = logger;
}
public List<ImportDecision> GetImportDecisions(IEnumerable<string> videoFiles, Series series, bool sceneSource)
public List<ImportDecision> GetImportDecisions(IEnumerable<string> videoFiles, Series series, bool sceneSource, QualityModel quality = null)
{
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series.Id);
_logger.Debug("Analysing {0}/{1} files.", newFiles.Count, videoFiles.Count());
return GetDecisions(newFiles, series, sceneSource).ToList();
return GetDecisions(newFiles, series, sceneSource, quality).ToList();
}
private IEnumerable<ImportDecision> GetDecisions(IEnumerable<String> videoFiles, Series series, bool sceneSource)
private IEnumerable<ImportDecision> GetDecisions(IEnumerable<String> videoFiles, Series series, bool sceneSource, QualityModel quality = null)
{
foreach (var file in videoFiles)
{
@ -59,6 +60,11 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
if (parsedEpisode != null)
{
if (quality != null && quality > parsedEpisode.Quality)
{
parsedEpisode.Quality = quality;
}
parsedEpisode.Size = _diskProvider.GetFileSize(file);
decision = GetDecision(parsedEpisode);
}

@ -1,4 +1,5 @@
using NzbDrone.Common.Messaging;
using System.Collections.Generic;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.Events
@ -6,10 +7,14 @@ namespace NzbDrone.Core.MediaFiles.Events
public class EpisodeDownloadedEvent : IEvent
{
public LocalEpisode Episode { get; private set; }
public EpisodeFile EpisodeFile { get; private set; }
public List<EpisodeFile> OldFiles { get; private set; }
public EpisodeDownloadedEvent(LocalEpisode episode)
public EpisodeDownloadedEvent(LocalEpisode episode, EpisodeFile episodeFile, List<EpisodeFile> oldFiles)
{
Episode = episode;
EpisodeFile = episodeFile;
OldFiles = oldFiles;
}
}
}

@ -61,7 +61,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
aBitRate = aBitRate.Remove(ABindex);
Int32.TryParse(aBitRate, out audioBitRate);
Int32.TryParse(mediaInfo.Get(StreamKind.General, 0, "PlayTime"), out runTime);
Int32.TryParse(mediaInfo.Get(StreamKind.Video, 0, "PlayTime"), out runTime);
Int32.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "StreamCount"), out streamCount);
string audioChannelsStr = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)");

@ -7,7 +7,7 @@ namespace NzbDrone.Core.MediaFiles
{
public interface IUpgradeMediaFiles
{
string UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
EpisodeFileMoveResult UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
}
public class UpgradeMediaFileService : IUpgradeMediaFiles
@ -31,8 +31,9 @@ namespace NzbDrone.Core.MediaFiles
_logger = logger;
}
public string UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
public EpisodeFileMoveResult UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
{
var moveFileResult = new EpisodeFileMoveResult();
var existingFiles = localEpisode.Episodes
.Where(e => e.EpisodeFileId > 0)
.Select(e => e.EpisodeFile.Value)
@ -48,11 +49,14 @@ namespace NzbDrone.Core.MediaFiles
_recycleBinProvider.DeleteFile(file.Path);
}
moveFileResult.OldFiles.Add(file);
_mediaFileService.Delete(file, true);
}
_logger.Trace("Moving episode file: {0}", episodeFile);
return _episodeFileMover.MoveEpisodeFile(episodeFile, localEpisode);
moveFileResult.Path = _episodeFileMover.MoveEpisodeFile(episodeFile, localEpisode);
return moveFileResult;
}
}
}

@ -25,6 +25,7 @@ namespace NzbDrone.Core.MetadataSource.Trakt
public int tvrage_id { get; set; }
public int last_updated { get; set; }
public string poster { get; set; }
public bool? ended { get; set; }
public Images images { get; set; }
public List<string> genres { get; set; }
public List<Season> seasons { get; set; }

@ -78,7 +78,7 @@ namespace NzbDrone.Core.MetadataSource
series.Network = show.network;
series.AirTime = show.air_time_utc;
series.TitleSlug = show.url.ToLower().Replace("http://trakt.tv/show/", "");
series.Status = GetSeriesStatus(show.status);
series.Status = GetSeriesStatus(show.status, show.ended);
series.Seasons = show.seasons.Select(s => new Tv.Season
{
@ -135,9 +135,17 @@ namespace NzbDrone.Core.MetadataSource
return withoutExtension + "-300" + extension;
}
private static SeriesStatusType GetSeriesStatus(string status)
private static SeriesStatusType GetSeriesStatus(string status, bool? ended)
{
if (string.IsNullOrWhiteSpace(status)) return SeriesStatusType.Continuing;
if (string.IsNullOrWhiteSpace(status))
{
if (ended.HasValue && ended.Value)
{
return SeriesStatusType.Ended;
}
return SeriesStatusType.Continuing;
}
if (status.Equals("Ended", StringComparison.InvariantCultureIgnoreCase)) return SeriesStatusType.Ended;
return SeriesStatusType.Continuing;
}

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications
{
public class DownloadMessage
{
public String Message { get; set; }
public Series Series { get; set; }
public EpisodeFile EpisodeFile { get; set; }
public List<EpisodeFile> OldFiles { get; set; }
public override string ToString()
{
return Message;
}
}
}

@ -25,10 +25,10 @@ namespace NzbDrone.Core.Notifications.Email
_smtpProvider.SendEmail(Settings, subject, body);
}
public override void OnDownload(string message, Series series)
public override void OnDownload(DownloadMessage message)
{
const string subject = "NzbDrone [TV] - Downloaded";
var body = String.Format("{0} Downloaded and sorted.", message);
var body = String.Format("{0} Downloaded and sorted.", message.Message);
_smtpProvider.SendEmail(Settings, subject, body);
}

@ -23,11 +23,11 @@ namespace NzbDrone.Core.Notifications.Growl
_growlProvider.SendNotification(title, message, "GRAB", Settings.Host, Settings.Port, Settings.Password);
}
public override void OnDownload(string message, Series series)
public override void OnDownload(DownloadMessage message)
{
const string title = "Episode Downloaded";
_growlProvider.SendNotification(title, message, "DOWNLOAD", Settings.Host, Settings.Port, Settings.Password);
_growlProvider.SendNotification(title, message.Message, "DOWNLOAD", Settings.Host, Settings.Port, Settings.Password);
}
public override void AfterRename(Series series)

@ -8,7 +8,7 @@ namespace NzbDrone.Core.Notifications
string Link { get; }
void OnGrab(string message);
void OnDownload(string message, Series series);
void OnDownload(DownloadMessage message);
void AfterRename(Series series);
}
}

@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications
public abstract string Link { get; }
public abstract void OnGrab(string message);
public abstract void OnDownload(string message, Series series);
public abstract void OnDownload(DownloadMessage message);
public abstract void AfterRename(Series series);
protected TSettings Settings

@ -73,13 +73,17 @@ namespace NzbDrone.Core.Notifications
public void Handle(EpisodeDownloadedEvent message)
{
var messageBody = GetMessage(message.Episode.Series, message.Episode.Episodes, message.Episode.ParsedEpisodeInfo.Quality);
var downloadMessage = new DownloadMessage();
downloadMessage.Message = GetMessage(message.Episode.Series, message.Episode.Episodes, message.Episode.ParsedEpisodeInfo.Quality);
downloadMessage.Series = message.Episode.Series;
downloadMessage.EpisodeFile = message.EpisodeFile;
downloadMessage.OldFiles = message.OldFiles;
foreach (var notification in _notificationFactory.OnDownloadEnabled())
{
try
{
notification.OnDownload(messageBody, message.Episode.Series);
notification.OnDownload(downloadMessage);
}
catch (Exception ex)

@ -23,11 +23,11 @@ namespace NzbDrone.Core.Notifications.NotifyMyAndroid
_notifyMyAndroidProxy.SendNotification(title, message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority);
}
public override void OnDownload(string message, Series series)
public override void OnDownload(DownloadMessage message)
{
const string title = "Episode Downloaded";
_notifyMyAndroidProxy.SendNotification(title, message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority);
_notifyMyAndroidProxy.SendNotification(title, message.Message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority);
}
public override void AfterRename(Series series)

@ -22,10 +22,10 @@ namespace NzbDrone.Core.Notifications.Plex
_plexProvider.Notify(Settings, header, message);
}
public override void OnDownload(string message, Series series)
public override void OnDownload(DownloadMessage message)
{
const string header = "NzbDrone [TV] - Downloaded";
_plexProvider.Notify(Settings, header, message);
_plexProvider.Notify(Settings, header, message.Message);
}
public override void AfterRename(Series series)

@ -20,7 +20,7 @@ namespace NzbDrone.Core.Notifications.Plex
{
}
public override void OnDownload(string message, Series series)
public override void OnDownload(DownloadMessage message)
{
UpdateIfEnabled();
}

@ -24,11 +24,11 @@ namespace NzbDrone.Core.Notifications.Prowl
_prowlProvider.SendNotification(title, message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
}
public override void OnDownload(string message, Series series)
public override void OnDownload(DownloadMessage message)
{
const string title = "Episode Downloaded";
_prowlProvider.SendNotification(title, message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
_prowlProvider.SendNotification(title, message.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
}
public override void AfterRename(Series series)

@ -23,11 +23,11 @@ namespace NzbDrone.Core.Notifications.PushBullet
_pushBulletProxy.SendNotification(title, message, Settings.ApiKey, Settings.DeviceId);
}
public override void OnDownload(string message, Series series)
public override void OnDownload(DownloadMessage message)
{
const string title = "Episode Downloaded";
_pushBulletProxy.SendNotification(title, message, Settings.ApiKey, Settings.DeviceId);
_pushBulletProxy.SendNotification(title, message.Message, Settings.ApiKey, Settings.DeviceId);
}
public override void AfterRename(Series series)

@ -23,11 +23,11 @@ namespace NzbDrone.Core.Notifications.Pushover
_pushoverProxy.SendNotification(title, message, Settings.UserKey, (PushoverPriority)Settings.Priority);
}
public override void OnDownload(string message, Series series)
public override void OnDownload(DownloadMessage message)
{
const string title = "Episode Downloaded";
_pushoverProxy.SendNotification(title, message, Settings.UserKey, (PushoverPriority)Settings.Priority);
_pushoverProxy.SendNotification(title, message.Message, Settings.UserKey, (PushoverPriority)Settings.Priority);
}
public override void AfterRename(Series series)

@ -119,7 +119,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
return String.Empty;
var xDoc = XDocument.Load(new StringReader(response.Replace("&", "&amp;")));
var xml = (from x in xDoc.Descendants("xml") select x).FirstOrDefault();
var xml = xDoc.Descendants("xml").Select(x => x).FirstOrDefault();
if (xml == null)
return null;

@ -1,4 +1,5 @@
using NzbDrone.Core.Tv;
using System.Linq;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Xbmc
{
@ -26,16 +27,16 @@ namespace NzbDrone.Core.Notifications.Xbmc
}
}
public override void OnDownload(string message, Series series)
public override void OnDownload(DownloadMessage message)
{
const string header = "NzbDrone [TV] - Downloaded";
if (Settings.Notify)
{
_xbmcProvider.Notify(Settings, header, message);
_xbmcProvider.Notify(Settings, header, message.Message);
}
UpdateAndClean(series);
UpdateAndClean(message.Series, message.OldFiles.Any());
}
public override void AfterRename(Series series)
@ -43,14 +44,14 @@ namespace NzbDrone.Core.Notifications.Xbmc
UpdateAndClean(series);
}
private void UpdateAndClean(Series series)
private void UpdateAndClean(Series series, bool clean = true)
{
if (Settings.UpdateLibrary)
{
_xbmcProvider.Update(Settings, series);
}
if (Settings.CleanLibrary)
if (clean && Settings.CleanLibrary)
{
_xbmcProvider.Clean(Settings);
}

@ -67,9 +67,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentMigrator.1.1.1.0\tools\FluentMigrator.Runner.dll</HintPath>
</Reference>
<Reference Include="FluentValidation, Version=4.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="FluentValidation, Version=5.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentValidation.4.0.0.1\lib\Net40\FluentValidation.dll</HintPath>
<HintPath>..\packages\FluentValidation.5.0.0.1\lib\Net40\FluentValidation.dll</HintPath>
</Reference>
<Reference Include="Growl.Connector">
<HintPath>..\packages\Growl.0.6\lib\Growl.Connector.dll</HintPath>
@ -83,11 +83,11 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="Omu.ValueInjecter">
<HintPath>..\packages\valueinjecter.2.3.3\lib\net35\Omu.ValueInjecter.dll</HintPath>
@ -96,9 +96,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Prowlin.0.9.4456.26422\lib\net40\Prowlin.dll</HintPath>
</Reference>
<Reference Include="RestSharp, Version=104.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="RestSharp, Version=104.3.3.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\RestSharp.104.2.0\lib\net4\RestSharp.dll</HintPath>
<HintPath>..\packages\RestSharp.104.3.3\lib\net4\RestSharp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -247,6 +247,7 @@
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedEpisodes.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedHistoryItems.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedEpisodeFiles.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupAdditionalNamingSpecs.cs" />
<Compile Include="Housekeeping\HousekeepingCommand.cs" />
<Compile Include="Housekeeping\HousekeepingService.cs" />
<Compile Include="Housekeeping\IHousekeepingTask.cs" />
@ -274,6 +275,7 @@
<Compile Include="Instrumentation\Commands\DeleteLogFilesCommand.cs" />
<Compile Include="Instrumentation\Commands\TrimLogCommand.cs" />
<Compile Include="Instrumentation\DeleteLogFilesService.cs" />
<Compile Include="MediaFiles\EpisodeFileMoveResult.cs" />
<Compile Include="MediaFiles\MediaFileExtensions.cs" />
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReader.cs" />
<Compile Include="Messaging\Commands\CommandExecutor.cs" />
@ -289,6 +291,7 @@
<Compile Include="MetadataSource\Trakt\TraktException.cs" />
<Compile Include="Notifications\NotificationFactory.cs" />
<Compile Include="Notifications\NotificationService.cs" />
<Compile Include="Notifications\DownloadMessage.cs" />
<Compile Include="Notifications\PushBullet\PushBullet.cs" />
<Compile Include="Notifications\PushBullet\PushBulletProxy.cs" />
<Compile Include="Notifications\PushBullet\PushBulletSettings.cs" />
@ -436,6 +439,7 @@
<Compile Include="Parser\Parser.cs" />
<Compile Include="Parser\ParsingService.cs" />
<Compile Include="Parser\QualityParser.cs" />
<Compile Include="RootFolders\RootFolderRepository.cs" />
<Compile Include="ThingiProvider\ConfigContractNotFoundException.cs" />
<Compile Include="ThingiProvider\IProvider.cs" />
<Compile Include="Qualities\QualityProfileInUseException.cs" />

@ -11,11 +11,27 @@ namespace NzbDrone.Core.Parser.Model
public QualityModel Quality { get; set; }
public int SeasonNumber { get; set; }
public int[] EpisodeNumbers { get; set; }
public int[] AbsoluteEpisodeNumbers { get; set; }
public String AirDate { get; set; }
public Language Language { get; set; }
public bool FullSeason { get; set; }
public ParsedEpisodeInfo()
{
EpisodeNumbers = new int[0];
AbsoluteEpisodeNumbers = new int[0];
}
public bool IsDaily()
{
return !String.IsNullOrWhiteSpace(AirDate);
}
public bool IsAbsoluteNumbering()
{
return AbsoluteEpisodeNumbers.Any();
}
public override string ToString()
{
string episodeString = "[Unknown Episode]";
@ -35,10 +51,5 @@ namespace NzbDrone.Core.Parser.Model
return string.Format("{0} - {1} {2}", SeriesTitle, episodeString, Quality);
}
public bool IsDaily()
{
return !String.IsNullOrWhiteSpace(AirDate);
}
}
}

@ -19,6 +19,13 @@ namespace NzbDrone.Core.Parser.Model
{
return DateTime.UtcNow.Subtract(PublishDate).Days;
}
//This prevents manually downloading a release from blowing up in mono
//TODO: Is there a better way?
private set
{
}
}
public string ReleaseGroup { get; set; }

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
@ -20,6 +21,22 @@ namespace NzbDrone.Core.Parser
new Regex(@"^(?<title>.+?)?\W*(?<airyear>\d{4})\W+(?<airmonth>[0-1][0-9])\W+(?<airday>[0-3][0-9])(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Anime - Absolute Episode Number + Title + Season+Episode
new Regex(@"^(?:(?<absoluteepisode>\d{2,3})(?:_|-|\s|\.)+)+(?<title>.+?)(?:\W|_)+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Anime - [SubGroup] Title Absolute Episode Number + Season+Episode
new Regex(@"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.))?(?<title>.+?)(?:(?:\W|_)+(?<absoluteepisode>\d{2,3}))+(?:_|-|\s|\.)+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Anime - [SubGroup] Title Season+Episode + Absolute Episode Number
new Regex(@"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.))?(?<title>.+?)(?:\W|_)+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)(?:\s|\.)(?:(?<absoluteepisode>\d{2,3})(?:_|-|\s|\.|$)+)+",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Anime - [SubGroup] Title Absolute Episode Number
new Regex(@"^\[(?<subgroup>.+?)\](?:_|-|\s|\.)?(?<title>.+?)(?:(?:\W|_)+(?<absoluteepisode>\d{2,}))+",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Multi-Part episodes without a title (S01E05.S01E06)
new Regex(@"^(?:\W*S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:[ex]){1,2}(?<episode>\d{1,3}(?!\d+)))+){2,}(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
@ -44,6 +61,10 @@ namespace NzbDrone.Core.Parser
new Regex(@"^(?<title>.*?)(?:\W?S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]){1,2}(?<episode>\d{1}))+)+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Anime - Title Absolute Episode Number [SubGroup]
new Regex(@"^(?<title>.+?)(?:(?:_|-|\s|\.)+(?<absoluteepisode>\d{2,3}))+(?:.+?)\[(?<subgroup>.+?)\](?:\.|$)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Supports 103/113 naming
new Regex(@"^(?<title>.+?)?(?:\W?(?<season>(?<!\d+)\d{1})(?<episode>\d{2}(?!p|i|\d+)))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
@ -53,7 +74,7 @@ namespace NzbDrone.Core.Parser
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Supports 1103/1113 naming
new Regex(@"^(?<title>.+?)?(?:\W?(?<season>(?<!\d+|\(|\[)\d{2})(?<episode>\d{2}(?!p|i|\d+|\)|\]|\W\d+)))+(\W+|_|$)(?!\\)",
new Regex(@"^(?<title>.+?)?(?:\W?(?<season>(?<!\d+|\(|\[|e|x)\d{2})(?<episode>(?<!e|x)\d{2}(?!p|i|\d+|\)|\]|\W\d+)))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Supports Season 01 Episode 03
@ -62,7 +83,20 @@ namespace NzbDrone.Core.Parser
//Supports Season only releases
new Regex(@"^(?<title>.+?)\W(?:S|Season)\W?(?<season>\d{1,2}(?!\d+))(\W+|_|$)(?<extras>EXTRAS|SUBPACK)?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled)
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//4-digit episode number
//Episodes without a title, Single (S01E05, 1x05) AND Multi (S01E04E05, 1x04x05, etc)
new Regex(@"^(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{4}(?!\d+|i|p)))+)(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Episodes with a title, Single episodes (S01E05, 1x05, etc) & Multi-episode (S01E05E06, S01E05-06, S01E05 E06, etc)
new Regex(@"^(?<title>.+?)(?:(\W|_)+S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{4}(?!\d+|i|p)))+)\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Anime - Title Absolute Episode Number
new Regex(@"^(?<title>.+?)(?:(?:_|-|\s|\.)+e(?<absoluteepisode>\d{2,3}))+",
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)",
@ -114,6 +148,7 @@ namespace NzbDrone.Core.Parser
if (match.Count != 0)
{
Debug.WriteLine(regex);
try
{
var result = ParseMatchCollection(match);
@ -226,11 +261,13 @@ namespace NzbDrone.Core.Parser
{
SeasonNumber = seasons.First(),
EpisodeNumbers = new int[0],
AbsoluteEpisodeNumbers = new int[0]
};
foreach (Match matchGroup in matchCollection)
{
var episodeCaptures = matchGroup.Groups["episode"].Captures.Cast<Capture>().ToList();
var absoluteEpisodeCaptures = matchGroup.Groups["absoluteepisode"].Captures.Cast<Capture>().ToList();
//Allows use to return a list of 0 episodes (We can handle that as a full season release)
if (episodeCaptures.Any())
@ -246,6 +283,20 @@ namespace NzbDrone.Core.Parser
var count = last - first + 1;
result.EpisodeNumbers = Enumerable.Range(first, count).ToArray();
}
if (absoluteEpisodeCaptures.Any())
{
var first = Convert.ToInt32(absoluteEpisodeCaptures.First().Value);
var last = Convert.ToInt32(absoluteEpisodeCaptures.Last().Value);
if (first > last)
{
return null;
}
var count = last - first + 1;
result.AbsoluteEpisodeNumbers = Enumerable.Range(first, count).ToArray();
}
else
{
//Check to see if this is an "Extras" or "SUBPACK" release, if it is, return NULL
@ -256,6 +307,10 @@ namespace NzbDrone.Core.Parser
result.FullSeason = true;
}
}
if (result.AbsoluteEpisodeNumbers.Any() && !result.EpisodeNumbers.Any())
{
result.SeasonNumber = 0;
}
}
else

@ -129,6 +129,26 @@ namespace NzbDrone.Core.Parser
return result;
}
if (parsedEpisodeInfo.IsAbsoluteNumbering())
{
foreach (var absoluteEpisodeNumber in parsedEpisodeInfo.AbsoluteEpisodeNumbers)
{
var episodeInfo = _episodeService.FindEpisode(series.Id, absoluteEpisodeNumber);
if (episodeInfo != null)
{
_logger.Info("Using absolute episode number {0} for: {1} - TVDB: {2}x{3:00}",
absoluteEpisodeNumber,
series.Title,
episodeInfo.SeasonNumber,
episodeInfo.EpisodeNumber);
result.Add(episodeInfo);
}
}
return result;
}
if (parsedEpisodeInfo.EpisodeNumbers == null)
return result;

@ -0,0 +1,27 @@
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.RootFolders
{
public interface IRootFolderRepository : IBasicRepository<RootFolder>
{
}
public class RootFolderRepository : BasicRepository<RootFolder>, IRootFolderRepository
{
public RootFolderRepository(IDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
}
protected override bool PublishModelEvents
{
get
{
return true;
}
}
}
}

@ -25,15 +25,15 @@ namespace NzbDrone.Core.RootFolders
public class RootFolderService : IRootFolderService
{
private static readonly Logger Logger = NzbDroneLogger.GetLogger();
private readonly IBasicRepository<RootFolder> _rootFolderRepository;
private readonly IRootFolderRepository _rootFolderRepository;
private readonly IDiskProvider _diskProvider;
private readonly ISeriesRepository _seriesRepository;
private readonly IConfigService _configService;
private static readonly HashSet<string> SpecialFolders = new HashSet<string> { "$recycle.bin", "system volume information", "recycler" };
private static readonly HashSet<string> SpecialFolders = new HashSet<string> { "$recycle.bin", "system volume information", "recycler", "lost+found" };
public RootFolderService(IBasicRepository<RootFolder> rootFolderRepository,
public RootFolderService(IRootFolderRepository rootFolderRepository,
IDiskProvider diskProvider,
ISeriesRepository seriesRepository,
IConfigService configService)

@ -11,6 +11,7 @@ namespace NzbDrone.Core.Tv
public interface IEpisodeRepository : IBasicRepository<Episode>
{
Episode Find(int seriesId, int season, int episodeNumber);
Episode Find(int seriesId, int absoluteEpisodeNumber);
Episode Get(int seriesId, String date);
Episode Find(int seriesId, String date);
List<Episode> GetEpisodes(int seriesId);
@ -39,6 +40,11 @@ namespace NzbDrone.Core.Tv
return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.SeasonNumber == season && s.EpisodeNumber == episodeNumber);
}
public Episode Find(int seriesId, int absoluteEpisodeNumber)
{
return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.AbsoluteEpisodeNumber == absoluteEpisodeNumber);
}
public Episode Get(int seriesId, String date)
{
return Query.Single(s => s.SeriesId == seriesId && s.AirDate == date);

@ -14,6 +14,7 @@ namespace NzbDrone.Core.Tv
{
Episode GetEpisode(int id);
Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber, bool useScene = false);
Episode FindEpisode(int seriesId, int absoluteEpisodeNumber);
Episode GetEpisode(int seriesId, String date);
Episode FindEpisode(int seriesId, String date);
List<Episode> GetEpisodeBySeries(int seriesId);
@ -62,6 +63,11 @@ namespace NzbDrone.Core.Tv
return _episodeRepository.Find(seriesId, seasonNumber, episodeNumber);
}
public Episode FindEpisode(int seriesId, int absoluteEpisodeNumber)
{
return _episodeRepository.Find(seriesId, absoluteEpisodeNumber);
}
public Episode GetEpisode(int seriesId, String date)
{
return _episodeRepository.Get(seriesId, date);

@ -106,7 +106,7 @@ namespace NzbDrone.Core.Tv
}
else
{
var allSeries = _seriesService.GetAllSeries().OrderBy(c => c.LastInfoSync).ToList();
var allSeries = _seriesService.GetAllSeries().OrderBy(c => c.Title).ToList();
foreach (var series in allSeries)
{

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentMigrator" version="1.1.1.0" targetFramework="net40" />
<package id="FluentValidation" version="4.0.0.1" targetFramework="net40" />
<package id="FluentValidation" version="5.0.0.1" targetFramework="net40" />
<package id="Growl" version="0.6" />
<package id="MediaInfoNet" version="0.3" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="Prowlin" version="0.9.4456.26422" targetFramework="net40" />
<package id="RestSharp" version="104.2.0" targetFramework="net40" />
<package id="RestSharp" version="104.3.3" targetFramework="net40" />
<package id="valueinjecter" version="2.3.3" targetFramework="net40" />
</packages>

@ -22,7 +22,6 @@ namespace NzbDrone.Host
AutoRegisterImplementations<NzbDronePersistentConnection>();
Container.Register(typeof(IBasicRepository<RootFolder>), typeof(BasicRepository<RootFolder>));
Container.Register(typeof(IBasicRepository<NamingConfig>), typeof(BasicRepository<NamingConfig>));
Container.Register<INancyBootstrapper, NancyBootstrapper>();

File diff suppressed because it is too large Load Diff

@ -89,21 +89,21 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.Hosting.1.1.0-beta2\lib\net40\Microsoft.Owin.Hosting.dll</HintPath>
</Reference>
<Reference Include="Nancy, Version=0.20.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Nancy, Version=0.21.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Nancy.0.20.0\lib\net40\Nancy.dll</HintPath>
<HintPath>..\packages\Nancy.0.21.1\lib\net40\Nancy.dll</HintPath>
</Reference>
<Reference Include="Nancy.Owin, Version=0.20.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Nancy.Owin, Version=0.21.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Nancy.Owin.0.20.0\lib\net40\Nancy.Owin.dll</HintPath>
<HintPath>..\packages\Nancy.Owin.0.21.1\lib\net40\Nancy.Owin.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>

@ -14,7 +14,7 @@ namespace NzbDrone.Host.Owin.MiddleWare
{
SignalrDependencyResolver.Register(container);
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(300000);
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromMinutes(3);
}
public void Attach(IAppBuilder appBuilder)

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@ -72,6 +72,10 @@ namespace NzbDrone.Host.Owin
try
{
// options.ServerFactory = new
//_host = WebApp.Start(OwinServiceProviderFactory.Create(), options, BuildApp);
//_host = WebApp.Start(options, BuildApp);
_host = WebApp.Start(OwinServiceProviderFactory.Create(), options, BuildApp);
}
catch (TargetInvocationException ex)

@ -5,11 +5,10 @@
<package id="Microsoft.Owin" version="1.1.0-beta2" targetFramework="net40" />
<package id="Microsoft.Owin.Host.HttpListener" version="1.1.0-beta2" targetFramework="net40" />
<package id="Microsoft.Owin.Hosting" version="1.1.0-beta2" targetFramework="net40" />
<package id="Nancy" version="0.20.0" targetFramework="net40" />
<package id="Nancy.Owin" version="0.20.0" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="NLog.Config" version="2.0.1.2" targetFramework="net40" />
<package id="NLog.Schema" version="2.0.1.2" targetFramework="net40" />
<package id="Nancy" version="0.21.1" targetFramework="net40" />
<package id="Nancy.Owin" version="0.21.1" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="NLog.Schema" version="2.1.0" targetFramework="net40" />
<package id="Owin" version="1.0" targetFramework="net40" />
</packages>
</packages>

@ -10,20 +10,28 @@ namespace NzbDrone.Integration.Test
[TestFixture]
public class EpisodeIntegrationTests : IntegrationTest
{
private SeriesResource series;
[SetUp]
public void Setup()
{
series = GivenSeriesWithEpisodes();
}
private SeriesResource GivenSeriesWithEpisodes()
{
var series = Series.Lookup("archer").First();
var newSeries = Series.Lookup("archer").First();
series.QualityProfileId = 1;
series.Path = @"C:\Test\Archer".AsOsAgnostic();
newSeries.QualityProfileId = 1;
newSeries.Path = @"C:\Test\Archer".AsOsAgnostic();
series = Series.Post(series);
newSeries = Series.Post(newSeries);
while (true)
{
if (Episodes.GetEpisodesInSeries(series.Id).Count > 0)
if (Episodes.GetEpisodesInSeries(newSeries.Id).Count > 0)
{
return series;
return newSeries;
}
Thread.Sleep(1000);
@ -33,28 +41,33 @@ namespace NzbDrone.Integration.Test
[Test]
public void should_be_able_to_get_all_episodes_in_series()
{
var series = GivenSeriesWithEpisodes();
Episodes.GetEpisodesInSeries(series.Id).Count.Should().BeGreaterThan(0);
}
[Test]
public void should_be_able_to_get_a_single_episode()
{
var series = GivenSeriesWithEpisodes();
var episodes = Episodes.GetEpisodesInSeries(series.Id);
Episodes.Get(episodes.First().Id).Should().NotBeNull();
}
[Test]
public void should_be_able_to_set_monitor_status_via_api()
public void should_be_able_to_set_monitor_status()
{
var series = GivenSeriesWithEpisodes();
var episodes = Episodes.GetEpisodesInSeries(series.Id);
var updatedEpisode = episodes.First();
updatedEpisode.Monitored = false;
Episodes.Put(updatedEpisode).Monitored.Should().BeFalse();
}
[TearDown]
public void TearDown()
{
Series.Delete(series.Id);
Thread.Sleep(2000);
}
}
}

@ -1,4 +1,9 @@
using NLog;
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNet.SignalR.Client.Transports;
using NLog;
using NLog.Config;
using NLog.Targets;
using NUnit.Framework;
@ -7,7 +12,10 @@ using NzbDrone.Api.Config;
using NzbDrone.Api.History;
using NzbDrone.Api.RootFolders;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Serializer;
using NzbDrone.Integration.Test.Client;
using NzbDrone.SignalR;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.Categories;
using RestSharp;
@ -29,6 +37,16 @@ namespace NzbDrone.Integration.Test
protected ClientBase<NamingConfigResource> NamingConfig;
private NzbDroneRunner _runner;
private List<SignalRMessage> _signalRReceived;
private Connection _signalrConnection;
protected IEnumerable<SignalRMessage> SignalRMessages
{
get
{
return _signalRReceived;
}
}
public IntegrationTest()
{
@ -40,8 +58,8 @@ namespace NzbDrone.Integration.Test
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, consoleTarget));
}
//[TestFixtureSetUp]
[SetUp]
[TestFixtureSetUp]
//[SetUp]
public void SmokeTestSetup()
{
_runner = new NzbDroneRunner();
@ -64,11 +82,61 @@ namespace NzbDrone.Integration.Test
NamingConfig = new ClientBase<NamingConfigResource>(RestClient, _runner.ApiKey, "config/naming");
}
//[TestFixtureTearDown]
[TearDown]
[TestFixtureTearDown]
//[TearDown]
public void SmokeTestTearDown()
{
_runner.KillAll();
}
[TearDown]
public void IntegrationSetup()
{
if (_signalrConnection != null)
{
switch (_signalrConnection.State)
{
case ConnectionState.Connected:
case ConnectionState.Connecting:
{
_signalrConnection.Stop();
break;
}
}
_signalrConnection = null;
_signalRReceived = new List<SignalRMessage>();
}
}
protected void ConnectSignalR()
{
_signalRReceived = new List<SignalRMessage>();
_signalrConnection = new Connection("http://localhost:8989/signalr");
_signalrConnection.Start(new LongPollingTransport()).ContinueWith(task =>
{
if (task.IsFaulted)
{
Assert.Fail("SignalrConnection failed. {0}", task.Exception.GetBaseException());
}
});
var retryCount = 0;
while (_signalrConnection.State != ConnectionState.Connected)
{
if (retryCount > 25)
{
Assert.Fail("Couldn't establish signalr connection. State: {0}", _signalrConnection.State);
}
retryCount++;
Console.WriteLine("Connecting to signalR" + _signalrConnection.State);
Thread.Sleep(200);
}
_signalrConnection.Received += json => _signalRReceived.Add(Json.Deserialize<SignalRMessage>(json)); ;
}
}
}

@ -37,11 +37,12 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll</HintPath>
</Reference>
<Reference Include="FluentValidation, Version=4.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="FluentValidation, Version=5.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentValidation.4.0.0.1\lib\Net40\FluentValidation.dll</HintPath>
<HintPath>..\packages\FluentValidation.5.0.0.1\lib\Net40\FluentValidation.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.SignalR.Client">
<Reference Include="Microsoft.AspNet.SignalR.Client, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.SignalR.Client.1.1.3\lib\net40\Microsoft.AspNet.SignalR.Client.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin">
@ -59,20 +60,21 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference>
<Reference Include="Nancy, Version=0.20.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Nancy, Version=0.21.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Nancy.0.20.0\lib\net40\Nancy.dll</HintPath>
<HintPath>..\packages\Nancy.0.21.1\lib\net40\Nancy.dll</HintPath>
</Reference>
<Reference Include="Nancy.Owin, Version=0.20.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Nancy.Owin, Version=0.21.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Nancy.Owin.0.20.0\lib\net40\Nancy.Owin.dll</HintPath>
<HintPath>..\packages\Nancy.Owin.0.21.1\lib\net40\Nancy.Owin.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.2\lib\nunit.framework.dll</HintPath>
@ -81,9 +83,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="RestSharp, Version=104.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="RestSharp, Version=104.3.3.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\RestSharp.104.2.0\lib\net4\RestSharp.dll</HintPath>
<HintPath>..\packages\RestSharp.104.3.3\lib\net4\RestSharp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -105,7 +107,6 @@
<Compile Include="EpisodeIntegrationTests.cs" />
<Compile Include="IndexerIntegrationFixture.cs" />
<Compile Include="IntegrationTestDirectoryInfo.cs" />
<Compile Include="NzbDroneRunner.cs" />
<Compile Include="QualityProfileIntegrationTest.cs" />
<Compile Include="ReleaseIntegrationTest.cs" />
<Compile Include="IntegrationTest.cs" />
@ -136,6 +137,10 @@
<Project>{95C11A9E-56ED-456A-8447-2C89C1139266}</Project>
<Name>NzbDrone.Host</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.SignalR\NzbDrone.SignalR.csproj">
<Project>{7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}</Project>
<Name>NzbDrone.SignalR</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.Test.Common\NzbDrone.Test.Common.csproj">
<Project>{CADDFCE0-7509-4430-8364-2074E1EEFCA2}</Project>
<Name>NzbDrone.Test.Common</Name>

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using FluentAssertions;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNet.SignalR.Client.Transports;
using NUnit.Framework;
using NzbDrone.Api.RootFolders;
@ -11,39 +8,25 @@ namespace NzbDrone.Integration.Test
[TestFixture]
public class RootFolderIntegrationTest : IntegrationTest
{
private Connection _connection;
private List<object> _signalRReceived;
[SetUp]
public void Setup()
{
_signalRReceived = new List<object>();
_connection = new Connection("http://localhost:8989/signalr/rootfolder");
_connection.Start(new LongPollingTransport()).ContinueWith(task =>
{
if (task.IsFaulted)
{
Assert.Fail("SignalrConnection failed. {0}", task.Exception.GetBaseException());
}
});
_connection.Received += _connection_Received;
}
private void _connection_Received(string obj)
[Test]
public void should_have_no_root_folder_initially()
{
_signalRReceived.Add(obj);
RootFolders.All().Should().BeEmpty();
}
[Test]
public void should_have_no_root_folder_initially()
public void should_add_and_delete_root_folders()
{
RootFolders.All().Should().BeEmpty();
ConnectSignalR();
var rootFolder = new RootFolderResource
{
Path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
};
{
Path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
};
var postResponse = RootFolders.Post(rootFolder);
@ -56,6 +39,11 @@ namespace NzbDrone.Integration.Test
RootFolders.Delete(postResponse.Id);
RootFolders.All().Should().BeEmpty();
SignalRMessages.Should().Contain(c => c.Name == "rootfolder");
}
[Test]

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="2.1.0.0" targetFramework="net40" />
<package id="FluentValidation" version="4.0.0.1" targetFramework="net40" />
<package id="FluentValidation" version="5.0.0.1" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.Client" version="1.1.3" targetFramework="net40" />
<package id="Microsoft.Owin" version="1.1.0-beta2" targetFramework="net40" />
<package id="Microsoft.Owin.Host.HttpListener" version="1.1.0-beta2" targetFramework="net40" />
<package id="Microsoft.Owin.Hosting" version="1.1.0-beta2" targetFramework="net40" />
<package id="Moq" version="4.0.10827" targetFramework="net40" />
<package id="Nancy" version="0.20.0" targetFramework="net40" />
<package id="Nancy.Owin" version="0.20.0" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="Nancy" version="0.21.1" targetFramework="net40" />
<package id="Nancy.Owin" version="0.21.1" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
<package id="Owin" version="1.0" targetFramework="net40" />
<package id="RestSharp" version="104.2.0" targetFramework="net40" />
<package id="RestSharp" version="104.3.3" targetFramework="net40" />
</packages>

@ -39,7 +39,7 @@
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.2\lib\nunit.framework.dll</HintPath>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="2.1.0.0" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
</packages>

@ -36,8 +36,9 @@
<Reference Include="Microsoft.AspNet.SignalR.Core">
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.1.1.3\lib\net40\Microsoft.AspNet.SignalR.Core.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.5.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.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AspNet.SignalR.Core" version="1.1.3" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net40" />
</packages>

@ -54,16 +54,20 @@
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.6.2.12296, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NUnit.2.6.2\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="RestSharp, Version=104.3.3.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\RestSharp.104.3.3\lib\net4\RestSharp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@ -82,6 +86,7 @@
<Compile Include="Categories\IntegrationTestAttribute.cs" />
<Compile Include="LoggingTest.cs" />
<Compile Include="MockerExtensions.cs" />
<Compile Include="NzbDroneRunner.cs" />
<Compile Include="ObjectExtentions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReflectionExtensions.cs" />

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

Loading…
Cancel
Save