Merge branch 'develop'

Conflicts:
	NzbDrone.Core/Tv/SeasonService.cs
	NzbDrone.Integration.Test/EpisodeIntegrationTests.cs
	NzbDrone.Integration.Test/SeasonIntegrationTests.cs
pull/38/merge
kay.one 11 years ago
commit 08203b1f6f

@ -149,7 +149,11 @@ namespace Exceptron.Client
SetHttpInfo(exceptionData, report);
SetEnviromentInfo(report);
return RestClient.Put<ExceptionResponse>(Configuration.Host, report);
var exceptionResponse = RestClient.Put<ExceptionResponse>(Configuration.Host, report);
exceptionData.Exception.Data["et"] = exceptionResponse.RefId;
return exceptionResponse;
}
catch (Exception e)
{

@ -36,9 +36,9 @@
<Reference Include="FizzWare.NBuilder">
<HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference>
<Reference Include="FluentAssertions, Version=2.0.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<Reference Include="FluentAssertions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
<HintPath>..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll</HintPath>
</Reference>
<Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="2.0.1" targetFramework="net40" />
<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" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
<package id="valueinjecter" version="2.3.3" targetFramework="net40" />
<package id="Moq" version="4.0.10827" />
</packages>

@ -25,7 +25,7 @@ namespace NzbDrone.Api.Frontend.Mappers
public override bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/Content") || resourceUrl.EndsWith(".js") || resourceUrl.EndsWith(".css");
return resourceUrl.StartsWith("/Content") || resourceUrl.EndsWith(".js") || resourceUrl.EndsWith(".css") || resourceUrl.EndsWith(".ico");
}
}
}

@ -164,6 +164,7 @@
<Compile Include="System\SystemModule.cs" />
<Compile Include="TinyIoCNancyBootstrapper.cs" />
<Compile Include="Update\UpdateModule.cs" />
<Compile Include="Validation\PathValidator.cs" />
<Compile Include="Validation\RuleBuilderExtensions.cs" />
</ItemGroup>
<ItemGroup>

@ -2,6 +2,7 @@
using System.Collections.Generic;
using NzbDrone.Core.RootFolders;
using NzbDrone.Api.Mapping;
using NzbDrone.Api.Validation;
namespace NzbDrone.Api.RootFolders
{
@ -17,6 +18,8 @@ namespace NzbDrone.Api.RootFolders
GetResourceById = GetRootFolder;
CreateResource = CreateRootFolder;
DeleteResource = DeleteFolder;
SharedValidator.RuleFor(c=>c.Path).IsValidPath();
}
private RootFolderResource GetRootFolder(int id)

@ -8,7 +8,7 @@ namespace NzbDrone.Api.RootFolders
public class RootFolderResource : RestResource
{
public String Path { get; set; }
public Int64 FreeSpace { get; set; }
public Int64? FreeSpace { get; set; }
public List<UnmappedFolder> UnmappedFolders { get; set; }
}

@ -31,10 +31,10 @@ namespace NzbDrone.Api.Series
SharedValidator.RuleFor(s => s.QualityProfileId).ValidId();
PutValidator.RuleFor(s => s.Path).NotEmpty();
PutValidator.RuleFor(s => s.Path).IsValidPath();
PostValidator.RuleFor(s => s.Path).NotEmpty().When(s => String.IsNullOrEmpty(s.RootFolderPath));
PostValidator.RuleFor(s => s.RootFolderPath).NotEmpty().When(s => String.IsNullOrEmpty(s.Path));
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => String.IsNullOrEmpty(s.RootFolderPath));
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => String.IsNullOrEmpty(s.Path));
PostValidator.RuleFor(s => s.Title).NotEmpty();
}

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using NzbDrone.Api.REST;
using NzbDrone.Core.Update;
using NzbDrone.Api.Mapping;
@ -32,7 +33,13 @@ namespace NzbDrone.Api.Update
public class UpdateResource : RestResource
{
public String Id { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
public Version Version { get; set; }
public String Branch { get; set; }
public DateTime ReleaseDate { get; set; }
public String FileName { get; set; }
public String Url { get; set; }
}

@ -0,0 +1,19 @@
using FluentValidation.Validators;
using NzbDrone.Common;
namespace NzbDrone.Api.Validation
{
public class PathValidator : PropertyValidator
{
public PathValidator()
: base("Invalid Path")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
if (context.PropertyValue == null) return false;
return context.PropertyValue.ToString().IsPathValid();
}
}
}

@ -1,4 +1,6 @@
using System.Text.RegularExpressions;
using System;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using FluentValidation;
using FluentValidation.Validators;
@ -20,5 +22,10 @@ namespace NzbDrone.Api.Validation
{
return ruleBuilder.SetValidator(new RegularExpressionValidator("^http(s)?://", RegexOptions.IgnoreCase)).WithMessage("must start with http:// or https://");
}
public static IRuleBuilderOptions<T, string> IsValidPath<T>(this IRuleBuilder<T, string> ruleBuilder)
{
return ruleBuilder.SetValidator(new PathValidator());
}
}
}

@ -37,9 +37,9 @@
<Reference Include="FizzWare.NBuilder, Version=3.0.1.0, Culture=neutral, PublicKeyToken=5651b03e12e42c12, processorArchitecture=MSIL">
<HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference>
<Reference Include="FluentAssertions, Version=2.0.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<Reference Include="FluentAssertions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
<HintPath>..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll</HintPath>
</Reference>
<Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="2.0.1" targetFramework="net40" />
<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" />

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Common.Test.DiskProviderTests
{
[TestFixture]
public class IsParentFixture : TestBase<DiskProvider>
{
private string _parent = @"C:\Test".AsOsAgnostic();
[Test]
public void should_return_false_when_not_a_child()
{
var path = @"C:\Another Folder".AsOsAgnostic();
Subject.IsParent(_parent, path).Should().BeFalse();
}
[Test]
public void should_return_true_when_folder_is_parent_of_another_folder()
{
var path = @"C:\Test\TV".AsOsAgnostic();
Subject.IsParent(_parent, path).Should().BeTrue();
}
[Test]
public void should_return_true_when_folder_is_parent_of_a_file()
{
var path = @"C:\Test\30.Rock.S01E01.Pilot.avi".AsOsAgnostic();
Subject.IsParent(_parent, path).Should().BeTrue();
}
}
}

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Test.Common;
@ -9,32 +6,38 @@ using NzbDrone.Test.Common;
namespace NzbDrone.Common.Test.DiskProviderTests
{
[TestFixture]
public class IsParentFixture : TestBase<DiskProvider>
public class FreeSpaceFixture : TestBase<DiskProvider>
{
private string _parent = @"C:\Test".AsOsAgnostic();
[Test]
public void should_return_false_when_not_a_child()
public void should_get_free_space_for_folder()
{
var path = @"C:\Another Folder".AsOsAgnostic();
var path = @"C:\".AsOsAgnostic();
Subject.IsParent(_parent, path).Should().BeFalse();
Subject.GetAvailableSpace(path).Should().NotBe(0);
}
[Test]
public void should_return_true_when_folder_is_parent_of_another_folder()
public void should_get_free_space_for_folder_that_doesnt_exist()
{
var path = @"C:\Test\TV".AsOsAgnostic();
var path = @"C:\".AsOsAgnostic();
Subject.IsParent(_parent, path).Should().BeTrue();
Subject.GetAvailableSpace(Path.Combine(path, "invalidFolder")).Should().NotBe(0);
}
[Test]
public void should_return_true_when_folder_is_parent_of_a_file()
public void should_get_free_space_for_drive_that_doesnt_exist()
{
var path = @"C:\Test\30.Rock.S01E01.Pilot.avi".AsOsAgnostic();
WindowsOnly();
Assert.Throws<DirectoryNotFoundException>(() => Subject.GetAvailableSpace("J:\\").Should().NotBe(0));
}
Subject.IsParent(_parent, path).Should().BeTrue();
[Test]
public void should_be_able_to_check_space_on_ramdrive()
{
LinuxOnly();
Subject.GetAvailableSpace("/run/").Should().NotBe(0);
}
}
}

@ -34,9 +34,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="FluentAssertions, Version=2.0.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<Reference Include="FluentAssertions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
<HintPath>..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll</HintPath>
</Reference>
<Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
@ -62,6 +62,7 @@
<Compile Include="CacheTests\CachedManagerFixture.cs" />
<Compile Include="CacheTests\CachedFixture.cs" />
<Compile Include="ConfigFileProviderTest.cs" />
<Compile Include="DiskProviderTests\FreeSpaceFixture.cs" />
<Compile Include="DiskProviderTests\IsParentFixture.cs" />
<Compile Include="EnsureTest\PathExtensionFixture.cs" />
<Compile Include="EnvironmentTests\StartupArgumentsFixture.cs" />

@ -102,30 +102,31 @@ namespace NzbDrone.Common.Test
}
[Test]
public void get_actual_casing_for_none_existing_file_should_throw()
public void get_actual_casing_for_none_existing_file_return_partially_fixed_result()
{
WindowsOnly();
Assert.Throws<DirectoryNotFoundException>(() => "C:\\InValidFolder\\invalidfile.exe".GetActualCasing());
"C:\\WINDOWS\\invalidfile.exe".GetActualCasing().Should().Be("C:\\Windows\\invalidfile.exe");
}
[Test]
public void get_actual_casing_should_return_actual_casing_for_local_file_in_windows()
public void get_actual_casing_for_none_existing_folder_return_partially_fixed_result()
{
WindowsOnly();
var path = Process.GetCurrentProcess().MainModule.FileName;
path.ToUpper().GetActualCasing().Should().Be(path);
path.ToLower().GetActualCasing().Should().Be(path);
"C:\\WINDOWS\\SYSTEM32\\FAKEFOLDER\\invalidfile.exe".GetActualCasing().Should().Be("C:\\Windows\\System32\\FAKEFOLDER\\invalidfile.exe");
}
[Test]
public void get_actual_casing_should_return_origibal_value_in_linux()
public void get_actual_casing_should_return_actual_casing_for_local_file_in_windows()
{
LinuxOnly();
WindowsOnly();
var path = Process.GetCurrentProcess().MainModule.FileName;
path.GetActualCasing().Should().Be(path);
path.GetActualCasing().Should().Be(path);
path.ToUpper().GetActualCasing().Should().Be(path);
path.ToLower().GetActualCasing().Should().Be(path);
}
[Test]
public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windows()
{

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="2.0.1" targetFramework="net40" />
<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="NUnit" version="2.6.2" targetFramework="net40" />

@ -17,7 +17,6 @@ namespace NzbDrone.Common
DateTime GetLastFolderWrite(string path);
DateTime GetLastFileWrite(string path);
void EnsureFolder(string path);
bool FolderExists(string path, bool caseSensitive);
bool FolderExists(string path);
bool FileExists(string path);
bool FileExists(string path, bool caseSensitive);
@ -32,7 +31,7 @@ namespace NzbDrone.Common
void MoveFile(string source, string destination);
void DeleteFolder(string path, bool recursive);
void InheritFolderPermissions(string filename);
long GetAvilableSpace(string path);
long? GetAvailableSpace(string path);
string ReadAllText(string filePath);
void WriteAllText(string filename, string contents);
void FileSetLastWriteTimeUtc(string path, DateTime dateTime);
@ -113,16 +112,6 @@ namespace NzbDrone.Common
return Directory.Exists(path);
}
public bool FolderExists(string path, bool caseSensitive)
{
if (caseSensitive)
{
return FolderExists(path) && path == path.GetActualCasing();
}
return FolderExists(path);
}
public bool FileExists(string path)
{
Ensure.That(() => path).IsValidPath();
@ -289,26 +278,37 @@ namespace NzbDrone.Common
File.SetAccessControl(filename, fs);
}
public long GetAvilableSpace(string path)
public long? GetAvailableSpace(string path)
{
Ensure.That(() => path).IsValidPath();
var root = GetPathRoot(path);
if (!FolderExists(root))
throw new DirectoryNotFoundException(root);
if (OsInfo.IsLinux)
{
var driveInfo = DriveInfo.GetDrives().SingleOrDefault(c => c.IsReady && path.StartsWith(c.Name, StringComparison.CurrentCultureIgnoreCase));
var drives = DriveInfo.GetDrives();
if (driveInfo == null)
foreach (var drive in drives)
{
throw new DirectoryNotFoundException(path);
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 driveInfo.AvailableFreeSpace;
return null;
}
var root = GetPathRoot(path);
if (!FolderExists(root))
throw new DirectoryNotFoundException(root);
return DriveFreeSpaceEx(root);
}

@ -95,8 +95,7 @@ namespace NzbDrone.Common.EnsureThat
return param;
}
private static readonly Regex windowsInvalidPathRegex = new Regex(@"[/*<>""|]", RegexOptions.Compiled);
private static readonly Regex windowsPathRegex = new Regex(@"^[a-zA-Z]:\\", RegexOptions.Compiled);
[DebuggerStepThrough]
public static Param<string> IsValidPath(this Param<string> param)
@ -104,31 +103,14 @@ namespace NzbDrone.Common.EnsureThat
if (string.IsNullOrWhiteSpace(param.Value))
throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNullOrWhiteSpace);
if (param.Value.IsPathValid()) return param;
if (OsInfo.IsLinux)
{
if (!param.Value.StartsWith(Path.DirectorySeparatorChar.ToString()))
{
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid *nix path. paths must start with /", param.Value));
}
}
else
{
if (windowsInvalidPathRegex.IsMatch(param.Value))
{
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid Windows path. It contains invalid characters", param.Value));
}
//Network path
if (param.Value.StartsWith(Path.DirectorySeparatorChar.ToString())) return param;
if (!windowsPathRegex.IsMatch(param.Value))
{
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid Windows path. paths must be a full path eg. C:\\Windows", param.Value));
}
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid *nix path. paths must start with /", param.Value));
}
return param;
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid Windows path. paths must be a full path eg. C:\\Windows", param.Value));
}
}
}

@ -93,6 +93,7 @@
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
<Compile Include="Instrumentation\ExceptronTarget.cs" />
<Compile Include="PathEqualityComparer.cs" />
<Compile Include="Services.cs" />
<Compile Include="TPL\LimitedConcurrencyLevelTaskScheduler.cs" />
<Compile Include="Security\IgnoreCertErrorPolicy.cs" />
<Compile Include="StringExtensions.cs" />

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.EnvironmentInfo;
@ -45,6 +46,29 @@ namespace NzbDrone.Common
return String.Equals(firstPath.CleanFilePath(), secondPath.CleanFilePath(), StringComparison.InvariantCultureIgnoreCase);
}
private static readonly Regex WindowsPathWithDriveRegex = new Regex(@"^[a-zA-Z]:\\", RegexOptions.Compiled);
public static bool IsPathValid(this string path)
{
if (path.ContainsInvalidPathChars() || string.IsNullOrWhiteSpace(path))
{
return false;
}
if (OsInfo.IsLinux)
{
return path.StartsWith(Path.DirectorySeparatorChar.ToString());
}
if (path.StartsWith("\\") || WindowsPathWithDriveRegex.IsMatch(path))
{
return true;
}
return false;
}
public static bool ContainsInvalidPathChars(this string text)
{
return text.IndexOfAny(Path.GetInvalidPathChars()) >= 0;
@ -58,28 +82,40 @@ namespace NzbDrone.Common
//Drive letter
return dirInfo.Name.ToUpper();
}
return Path.Combine(GetProperCapitalization(parentDirInfo), parentDirInfo.GetDirectories(dirInfo.Name)[0].Name);
var folderName = dirInfo.Name;
if (dirInfo.Exists)
{
folderName = parentDirInfo.GetDirectories(dirInfo.Name)[0].Name;
}
return Path.Combine(GetProperCapitalization(parentDirInfo), folderName);
}
public static string GetActualCasing(this string path)
{
var attributes = File.GetAttributes(path);
if (OsInfo.IsLinux || path.StartsWith("\\"))
{
return path;
}
if ((attributes & FileAttributes.Directory) == FileAttributes.Directory)
if (Directory.Exists(path) && (File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory)
{
return GetProperCapitalization(new DirectoryInfo(path));
}
var fileInfo = new FileInfo(path);
var dirInfo = fileInfo.Directory;
var fileName = fileInfo.Name;
if (dirInfo != null && fileInfo.Exists)
{
fileName = dirInfo.GetFiles(fileInfo.Name)[0].Name;
}
DirectoryInfo dirInfo = fileInfo.Directory;
return Path.Combine(GetProperCapitalization(dirInfo), dirInfo.GetFiles(fileInfo.Name)[0].Name);
return Path.Combine(GetProperCapitalization(dirInfo), fileName);
}
public static string GetAppDataPath(this IAppFolderInfo appFolderInfo)

@ -40,7 +40,6 @@ namespace NzbDrone.Common.Serializer
return JsonConvert.SerializeObject(obj);
}
public static void Serialize<TModel>(TModel model, TextWriter outputStream)
{
var jsonTextWriter = new JsonTextWriter(outputStream);
@ -52,7 +51,5 @@ namespace NzbDrone.Common.Serializer
{
Serialize(model, new StreamWriter(outputStream));
}
}
}

@ -0,0 +1,15 @@
using System;
namespace NzbDrone.Common
{
public class Services
{
public static String RootUrl
{
get
{
return "http://services.nzbdrone.com";
}
}
}
}

@ -1,5 +1,6 @@
using System;
using System.Threading;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Host;
@ -18,7 +19,6 @@ namespace NzbDrone.Console
}
catch (Exception e)
{
System.Console.WriteLine(e.ToString());
System.Console.ReadLine();
}

@ -15,14 +15,6 @@ namespace NzbDrone.Core.Test.DataAugmentationFixture.Scene
{
private const string SCENE_MAPPING_URL = "http://services.nzbdrone.com/SceneMapping/Active";
[SetUp]
public void Setup()
{
Mocker.GetMock<IConfigService>().SetupGet(s => s.ServiceRootUrl)
.Returns("http://services.nzbdrone.com");
}
[Test]
public void fetch_should_return_list_of_mappings()
{

@ -44,17 +44,17 @@ namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests
[Test]
[Explicit("needs newznab api key")]
public void nzbsorg_rss()
{
var indexer = new Newznab();
indexer.Settings = new NewznabSettings
{
ApiKey = "",
ApiKey = "64d61d3cfd4b75e51d01cbc7c6a78275",
Url = "http://nzbs.org"
};
indexer.InstanceDefinition = new IndexerDefinition();
indexer.InstanceDefinition.Name = "nzbs.org";
var result = Subject.FetchRss(indexer);

@ -54,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests
private void GivenFreeSpace(long size)
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(It.IsAny<String>()))
.Setup(s => s.GetAvailableSpace(It.IsAny<String>()))
.Returns(size);
}
@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.GetAvilableSpace(_rootFolder), Times.Once());
.Verify(v => v.GetAvailableSpace(_rootFolder), Times.Once());
}
}
}

@ -53,8 +53,7 @@ namespace NzbDrone.Core.Test.MediaFileTests
}
Mocker.GetMock<IUpgradeMediaFiles>()
.Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>()))
.Returns(new EpisodeFile());
.Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>()));
}
[Test]

@ -60,8 +60,7 @@ namespace NzbDrone.Core.Test.MediaFileTests
private void GivenMovedFiles()
{
Mocker.GetMock<IMoveEpisodeFiles>()
.Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), _series))
.Returns(_episodeFiles.First());
.Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), _series));
}
[Test]

@ -43,9 +43,9 @@
<Reference Include="FizzWare.NBuilder, Version=3.0.1.0, Culture=neutral, PublicKeyToken=5651b03e12e42c12, processorArchitecture=MSIL">
<HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference>
<Reference Include="FluentAssertions, Version=2.0.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<Reference Include="FluentAssertions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
<HintPath>..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll</HintPath>
</Reference>
<Reference Include="FluentMigrator, Version=1.1.1.0, Culture=neutral, PublicKeyToken=aacfc7de5acabf05, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@ -186,6 +186,7 @@
<Compile Include="ProviderTests\DiskProviderTests\ArchiveProviderFixture.cs" />
<Compile Include="MediaFileTests\DropFolderImportServiceFixture.cs" />
<Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" />
<Compile Include="TvTests\SeasonServiceTests\HandleEpisodeInfoDeletedEventFixture.cs" />
<Compile Include="TvTests\SeasonServiceTests\SetSeasonPassFixture.cs" />
<Compile Include="TvTests\SeasonServiceTests\SetMonitoredFixture.cs" />
<Compile Include="TvTests\SeriesRepositoryTests\QualityProfileRepositoryFixture.cs" />

@ -353,6 +353,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("H.Polukatoikia.S03E13.Greek.PDTV.XviD-Ouzo", Language.Greek)]
[TestCase("Burn.Notice.S04E15.Brotherly.Love.GERMAN.DUBBED.WS.WEBRiP.XviD.REPACK-TVP", Language.German)]
[TestCase("Ray Donovan - S01E01.720p.HDtv.x264-Evolve (NLsub)", Language.Norwegian)]
[TestCase("Shield,.The.1x13.Tueurs.De.Flics.FR.DVDRip.XviD", Language.French)]
public void parse_language(string postTitle, Language language)
{
var result = Parser.Parser.ParseTitle(postTitle);

@ -12,74 +12,118 @@ namespace NzbDrone.Core.Test.ParserTests
public class QualityParserFixture : CoreTest
{
public static object[] QualityParserCases =
{
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", Quality.DVD, false },
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.X-viD.AC3.-HELLYWOOD", Quality.DVD, false },
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", Quality.DVD, false },
new object[] { "Two.and.a.Half.Men.S08E05.720p.HDTV.X264-DIMENSION", Quality.HDTV720p, false },
new object[] { "Chuck S11E03 has no periods or extension HDTV", Quality.SDTV, false },
new object[] { "Chuck.S04E05.HDTV.XviD-LOL", Quality.SDTV, false },
new object[] { "The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", Quality.DVD, false },
new object[] { "The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", Quality.DVD, false },
new object[] { "The.Girls.Next.Door.S03E06.HDTV-WiDE", Quality.SDTV, false },
new object[] { "Degrassi.S10E27.WS.DSR.XviD-2HD", Quality.SDTV, false },
new object[] { "Sonny.With.a.Chance.S02E15.720p.WEB-DL.DD5.1.H.264-SURFER", Quality.WEBDL720p, false },
new object[] { "Sonny.With.a.Chance.S02E15.720p", Quality.HDTV720p, false },
new object[] { "Sonny.With.a.Chance.S02E15.mkv", Quality.HDTV720p, false },
new object[] { "Sonny.With.a.Chance.S02E15.avi", Quality.SDTV, false },
new object[] { "Sonny.With.a.Chance.S02E15.xvid", Quality.SDTV, false },
new object[] { "Sonny.With.a.Chance.S02E15.divx", Quality.SDTV, false },
new object[] { "Sonny.With.a.Chance.S02E15", Quality.Unknown, false },
new object[] { "Chuck - S01E04 - So Old - Playdate - 720p TV.mkv", Quality.HDTV720p, false },
new object[] { "Chuck - S22E03 - MoneyBART - HD TV.mkv", Quality.HDTV720p, false },
new object[] { "Chuck - S01E03 - Come Fly With Me - 720p BluRay.mkv", Quality.Bluray720p, false },
new object[] { "Chuck - S01E03 - Come Fly With Me - 1080p BluRay.mkv", Quality.Bluray1080p, false },
new object[] { "Chuck - S11E06 - D-Yikes! - 720p WEB-DL.mkv", Quality.WEBDL720p, false },
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", Quality.DVD, false },
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", Quality.DVD, false },
new object[] { "Law & Order: Special Victims Unit - 11x11 - Quickie", Quality.Unknown, false },
new object[] { "S07E23 - [HDTV-720p].mkv ", Quality.HDTV720p, false },
new object[] { "S07E23 - [WEBDL].mkv ", Quality.WEBDL720p, false },
new object[] { "S07E23.mkv ", Quality.HDTV720p, false },
new object[] { "S07E23 .avi ", Quality.SDTV, false },
new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3.-HELLYWOOD.avi", Quality.DVD, false },
new object[] { "WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", Quality.Bluray720p, false },
new object[] { "The Voice S01E11 The Finals 1080i HDTV DD5.1 MPEG2-TrollHD", Quality.RAWHD, false },
new object[] { "Nikita S02E01 HDTV XviD 2HD", Quality.SDTV, false },
new object[] { "Gossip Girl S05E11 PROPER HDTV XviD 2HD", Quality.SDTV, true },
new object[] { "The Jonathan Ross Show S02E08 HDTV x264 FTP", Quality.SDTV, false },
new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-TLA", Quality.SDTV, false },
new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA", Quality.SDTV, true },
new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", Quality.DVD, true },
new object[] { "Pawn Stars S04E87 REPACK 720p HDTV x264 aAF", Quality.HDTV720p, true },
new object[] { "The Real Housewives of Vancouver S01E04 DSR x264 2HD", Quality.SDTV, false },
new object[] { "Vanguard S01E04 Mexicos Death Train DSR x264 MiNDTHEGAP", Quality.SDTV, false },
new object[] { "Vanguard S01E04 Mexicos Death Train 720p WEB DL", Quality.WEBDL720p, false },
new object[] { "Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", Quality.WEBDL720p, false },
new object[] { "Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", Quality.WEBDL720p, false },
new object[] { "Fringe S04E22 720p WEB-DL DD5.1 H264-EbP.mkv", Quality.WEBDL720p, false },
new object[] { "CSI NY S09E03 1080p WEB DL DD5 1 H264 NFHD", Quality.WEBDL1080p, false },
new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 NFHD", Quality.WEBDL1080p, false },
new object[] { "Criminal.Minds.S08E01.1080p.WEB-DL.DD5.1.H264-NFHD", Quality.WEBDL1080p, false },
new object[] { "Its.Always.Sunny.in.Philadelphia.S08E01.1080p.WEB-DL.proper.AAC2.0.H.264", Quality.WEBDL1080p, true },
new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 REPACK NFHD", Quality.WEBDL1080p, true },
new object[] { "Glee.S04E09.Swan.Song.1080p.WEB-DL.DD5.1.H.264-ECI", Quality.WEBDL1080p, false },
new object[] { "Elementary.S01E10.The.Leviathan.480p.WEB-DL.x264-mSD", Quality.WEBDL480p, false },
new object[] { "Glee.S04E10.Glee.Actually.480p.WEB-DL.x264-mSD", Quality.WEBDL480p, false },
new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.480p.WEB-DL.x264-mSD", Quality.WEBDL480p, false },
new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.1080p.WEB-DL.DD5.1.H.264", Quality.WEBDL1080p, false },
new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.X264-QCF", Quality.HDTV1080p, false },
new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.x264-QCF", Quality.HDTV1080p, false },
new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.proper.X264-QCF", Quality.HDTV1080p, true },
new object[] { "Dexter - S01E01 - Title [HDTV]", Quality.HDTV720p, false },
new object[] { "Dexter - S01E01 - Title [HDTV-720p]", Quality.HDTV720p, false },
new object[] { "Dexter - S01E01 - Title [HDTV-1080p]", Quality.HDTV1080p, false },
new object[] { "POI S02E11 1080i HDTV DD5.1 MPEG2-TrollHD", Quality.RAWHD, false },
new object[] { "How I Met Your Mother S01E18 Nothing Good Happens After 2 A.M. 720p HDTV DD5.1 MPEG2-TrollHD", Quality.RAWHD, false },
new object[] { "Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", Quality.WEBDL1080p, false },
new object[] { "Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", Quality.WEBDL720p, false },
new object[] { "Sons.Of.Anarchy.S02E13.1080p.BluRay.x264-AVCDVD", Quality.Bluray1080p, false }
public static object[] SdtvCases =
{
new object[] { "S07E23 .avi ", false },
new object[] {"The.Shield.S01E13.x264-CtrlSD", false},
new object[] { "Nikita S02E01 HDTV XviD 2HD", false },
new object[] { "Gossip Girl S05E11 PROPER HDTV XviD 2HD", true },
new object[] { "The Jonathan Ross Show S02E08 HDTV x264 FTP", false },
new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-TLA", false },
new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA", true },
new object[] { "The Real Housewives of Vancouver S01E04 DSR x264 2HD", false },
new object[] { "Vanguard S01E04 Mexicos Death Train DSR x264 MiNDTHEGAP", false },
new object[] { "Chuck S11E03 has no periods or extension HDTV", false },
new object[] { "Chuck.S04E05.HDTV.XviD-LOL", false },
new object[] { "Sonny.With.a.Chance.S02E15.avi", false },
new object[] { "Sonny.With.a.Chance.S02E15.xvid", false },
new object[] { "Sonny.With.a.Chance.S02E15.divx", false },
new object[] { "The.Girls.Next.Door.S03E06.HDTV-WiDE", false },
new object[] { "Degrassi.S10E27.WS.DSR.XviD-2HD", false },
};
public static object[] DvdCases =
{
new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", true },
new object[] { "The.Shield.S01E13.NTSC.x264-CtrlSD", false },
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", false },
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.X-viD.AC3.-HELLYWOOD", false },
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", false },
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false },
new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false },
new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3.-HELLYWOOD.avi", false },
new object[] { "The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", false },
new object[] { "The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", false },
new object[] { "the.shield.1x13.circles.ws.xvidvd-tns", false}
};
public static object[] Webdl480pCases =
{
new object[] { "Elementary.S01E10.The.Leviathan.480p.WEB-DL.x264-mSD", false },
new object[] { "Glee.S04E10.Glee.Actually.480p.WEB-DL.x264-mSD", false },
new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.480p.WEB-DL.x264-mSD", false },
};
public static object[] Hdtv720pCases =
{
new object[] { "Dexter - S01E01 - Title [HDTV]", false },
new object[] { "Dexter - S01E01 - Title [HDTV-720p]", false },
new object[] { "Pawn Stars S04E87 REPACK 720p HDTV x264 aAF", true },
new object[] { "Sonny.With.a.Chance.S02E15.720p", false },
new object[] { "S07E23 - [HDTV-720p].mkv ", false },
new object[] { "Chuck - S22E03 - MoneyBART - HD TV.mkv", false },
new object[] { "S07E23.mkv ", false },
new object[] { "Two.and.a.Half.Men.S08E05.720p.HDTV.X264-DIMENSION", false },
new object[] { "Sonny.With.a.Chance.S02E15.mkv", false },
};
public static object[] Hdtv1080pCases =
{
new object[] { "Under the Dome S01E10 Let the Games Begin 1080p", false },
new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.X264-QCF", false },
new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.x264-QCF", false },
new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.proper.X264-QCF", true },
new object[] { "Dexter - S01E01 - Title [HDTV-1080p]", false },
};
public static object[] Webdl720pCases =
{
new object[] { "Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", false },
new object[] { "Vanguard S01E04 Mexicos Death Train 720p WEB DL", false },
new object[] { "Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", false },
new object[] { "Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", false },
new object[] { "Chuck - S11E06 - D-Yikes! - 720p WEB-DL.mkv", false },
new object[] { "Sonny.With.a.Chance.S02E15.720p.WEB-DL.DD5.1.H.264-SURFER", false },
new object[] { "S07E23 - [WEBDL].mkv ", false },
new object[] { "Fringe S04E22 720p WEB-DL DD5.1 H264-EbP.mkv", false },
};
public static object[] Webdl1080pCases =
{
new object[] { "Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false },
new object[] { "CSI NY S09E03 1080p WEB DL DD5 1 H264 NFHD", false },
new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 NFHD", false },
new object[] { "Criminal.Minds.S08E01.1080p.WEB-DL.DD5.1.H264-NFHD", false },
new object[] { "Its.Always.Sunny.in.Philadelphia.S08E01.1080p.WEB-DL.proper.AAC2.0.H.264", true },
new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 REPACK NFHD", true },
new object[] { "Glee.S04E09.Swan.Song.1080p.WEB-DL.DD5.1.H.264-ECI", false },
new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.1080p.WEB-DL.DD5.1.H.264", false },
};
public static object[] Bluray720pCases =
{
new object[] { "WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", false },
new object[] { "Chuck - S01E03 - Come Fly With Me - 720p BluRay.mkv", false },
};
public static object[] Bluray1080pCases =
{
new object[] { "Chuck - S01E03 - Come Fly With Me - 1080p BluRay.mkv", false },
new object[] { "Sons.Of.Anarchy.S02E13.1080p.BluRay.x264-AVCDVD", false },
};
public static object[] RawCases =
{
new object[] { "POI S02E11 1080i HDTV DD5.1 MPEG2-TrollHD", false },
new object[] { "How I Met Your Mother S01E18 Nothing Good Happens After 2 A.M. 720p HDTV DD5.1 MPEG2-TrollHD", false },
new object[] { "The Voice S01E11 The Finals 1080i HDTV DD5.1 MPEG2-TrollHD", false },
};
public static object[] UnknownCases =
{
new object[] { "Sonny.With.a.Chance.S02E15", false },
new object[] { "Law & Order: Special Victims Unit - 11x11 - Quickie", false },
};
public static object[] SelfQualityParserCases =
@ -94,20 +138,100 @@ namespace NzbDrone.Core.Test.ParserTests
new object[] { Quality.Bluray1080p }
};
[Test, TestCaseSource("QualityParserCases")]
public void quality_parse(string postTitle, Quality quality, bool proper)
[Test, TestCaseSource("SdtvCases")]
public void should_parse_sdtv_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.SDTV);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("DvdCases")]
public void should_parse_dvd_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.DVD);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("Webdl480pCases")]
public void should_parse_webdl480p_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.WEBDL480p);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("Hdtv720pCases")]
public void should_parse_hdtv720p_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.HDTV720p);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("Hdtv1080pCases")]
public void should_parse_hdtv1080p_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.HDTV1080p);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("Webdl720pCases")]
public void should_parse_webdl720p_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.WEBDL720p);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("Webdl1080pCases")]
public void should_parse_webdl1080p_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.WEBDL1080p);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("Bluray720pCases")]
public void should_parse_bluray720p_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.Bluray720p);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("Bluray1080pCases")]
public void should_parse_bluray1080p_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.Bluray1080p);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("RawCases")]
public void should_parse_raw_quality(string postTitle, bool proper)
{
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.RAWHD);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("UnknownCases")]
public void quality_parse(string postTitle, bool proper)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Quality.Quality.Should().Be(quality);
result.Quality.Proper.Should().Be(proper);
var result = Parser.QualityParser.ParseQuality(postTitle);
result.Quality.Should().Be(Quality.Unknown);
result.Proper.Should().Be(proper);
}
[Test, TestCaseSource("SelfQualityParserCases")]
public void parsing_our_own_quality_enum(Quality quality)
{
var fileName = String.Format("My series S01E01 [{0}]", quality);
var result = Parser.Parser.ParseTitle(fileName);
result.Quality.Quality.Should().Be(quality);
var result = Parser.QualityParser.ParseQuality(fileName);
result.Quality.Should().Be(quality);
}
}
}

@ -14,7 +14,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests
[Test]
public void should_return_free_disk_space()
{
var result = Subject.GetAvilableSpace(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
var result = Subject.GetAvailableSpace(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
result.Should().BeGreaterThan(0);
}
@ -23,7 +23,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests
{
WindowsOnly();
var result = Subject.GetAvilableSpace(@"\\localhost\c$\Windows");
var result = Subject.GetAvailableSpace(@"\\localhost\c$\Windows");
result.Should().BeGreaterThan(0);
}
@ -32,13 +32,13 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests
{
WindowsOnly();
Assert.Throws<DirectoryNotFoundException>(() => Subject.GetAvilableSpace(@"Z:\NOT_A_REAL_PATH\DOES_NOT_EXIST".AsOsAgnostic()));
Assert.Throws<DirectoryNotFoundException>(() => Subject.GetAvailableSpace(@"Z:\NOT_A_REAL_PATH\DOES_NOT_EXIST".AsOsAgnostic()));
}
[Test]
public void should_be_able_to_get_space_on_folder_that_doesnt_exist()
{
var result = Subject.GetAvilableSpace(@"C:\I_DO_NOT_EXIST".AsOsAgnostic());
var result = Subject.GetAvailableSpace(@"C:\I_DO_NOT_EXIST".AsOsAgnostic());
result.Should().BeGreaterThan(0);
}
}

@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
.Returns(@"C:\");
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(@"C:\"))
.Setup(s => s.GetAvailableSpace(@"C:\"))
.Returns(123456);
var result = Subject.FreeSpaceOnDrives();
@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
.Returns(@"C:\");
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(@"C:\"))
.Setup(s => s.GetAvailableSpace(@"C:\"))
.Returns(123456);
var result = Subject.FreeSpaceOnDrives();
@ -76,7 +76,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
.Returns(@"D:\");
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(It.IsAny<string>()))
.Setup(s => s.GetAvailableSpace(It.IsAny<string>()))
.Returns(123456);
var result = Subject.FreeSpaceOnDrives();
@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
.Returns(@"C:\");
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(It.IsAny<string>()))
.Setup(s => s.GetAvailableSpace(It.IsAny<string>()))
.Throws(new DirectoryNotFoundException());
var result = Subject.FreeSpaceOnDrives();

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.Test.TvTests.SeasonServiceTests
{
[TestFixture]
public class HandleEpisodeInfoDeletedEventFixture : CoreTest<SeasonService>
{
private List<Season> _seasons;
private List<Episode> _episodes;
[SetUp]
public void Setup()
{
_seasons = Builder<Season>
.CreateListOfSize(1)
.All()
.With(s => s.SeriesId = 1)
.Build()
.ToList();
_episodes = Builder<Episode>
.CreateListOfSize(1)
.All()
.With(e => e.SeasonNumber = _seasons.First().SeasonNumber)
.With(s => s.SeriesId = _seasons.First().SeasonNumber)
.Build()
.ToList();
Mocker.GetMock<ISeasonRepository>()
.Setup(s => s.GetSeasonBySeries(It.IsAny<int>()))
.Returns(_seasons);
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodesBySeason(It.IsAny<int>(), _seasons.First().SeasonNumber))
.Returns(_episodes);
}
private void GivenAbandonedSeason()
{
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodesBySeason(It.IsAny<int>(), _seasons.First().SeasonNumber))
.Returns(new List<Episode>());
}
[Test]
public void should_not_delete_when_season_is_still_valid()
{
Subject.Handle(new EpisodeInfoDeletedEvent(_episodes));
Mocker.GetMock<ISeasonRepository>()
.Verify(v => v.Delete(It.IsAny<Season>()), Times.Never());
}
[Test]
public void should_delete_season_if_no_episodes_exist_in_that_season()
{
GivenAbandonedSeason();
Subject.Handle(new EpisodeInfoDeletedEvent(_episodes));
Mocker.GetMock<ISeasonRepository>()
.Verify(v => v.Delete(It.IsAny<Season>()), Times.Once());
}
[Test]
public void should_only_delete_a_season_once()
{
_episodes = Builder<Episode>
.CreateListOfSize(5)
.All()
.With(e => e.SeasonNumber = _seasons.First().SeasonNumber)
.With(s => s.SeriesId = _seasons.First().SeasonNumber)
.Build()
.ToList();
GivenAbandonedSeason();
Subject.Handle(new EpisodeInfoDeletedEvent(_episodes));
Mocker.GetMock<ISeasonRepository>()
.Verify(v => v.Delete(It.IsAny<Season>()), Times.Once());
}
}
}

@ -16,13 +16,7 @@ namespace NzbDrone.Core.Test.UpdateTests
Mocker.GetMock<IConfigFileProvider>().SetupGet(c => c.Branch).Returns("master");
var updates = Subject.GetAvailablePackages().ToList();
updates.Should().NotBeEmpty();
updates.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.FileName));
updates.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.Url));
updates.Should().OnlyContain(c => c.Version != null);
updates.Should().OnlyContain(c => c.Version.Major == 2);
Subject.GetLatestUpdate().Should().BeNull();
}
}
}

@ -2,7 +2,7 @@
<packages>
<package id="AutoMoq" version="1.6.1" targetFramework="net40" />
<package id="CommonServiceLocator" version="1.0" targetFramework="net40" />
<package id="FluentAssertions" version="2.0.1" 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="Moq" version="4.0.10827" />

@ -172,11 +172,6 @@ namespace NzbDrone.Core.Configuration
set { SetValue("BlackholeFolder", value); }
}
public string ServiceRootUrl
{
get { return "http://services.nzbdrone.com"; }
}
public string PneumaticFolder
{
get { return GetValue("PneumaticFolder", String.Empty); }

@ -25,7 +25,6 @@ namespace NzbDrone.Core.Configuration
int Retention { get; set; }
DownloadClientType DownloadClient { get; set; }
string BlackholeFolder { get; set; }
string ServiceRootUrl { get; }
string PneumaticFolder { get; set; }
string RecycleBin { get; set; }
String NzbgetUsername { get; set; }

@ -3,7 +3,6 @@ using System.Collections.Generic;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.DataAugmentation.DailySeries
{
@ -17,13 +16,11 @@ namespace NzbDrone.Core.DataAugmentation.DailySeries
public class DailySeriesDataProxy : IDailySeriesDataProxy
{
private readonly IHttpProvider _httpProvider;
private readonly IConfigService _configService;
private readonly Logger _logger;
public DailySeriesDataProxy(IHttpProvider httpProvider, IConfigService configService, Logger logger)
public DailySeriesDataProxy(IHttpProvider httpProvider, Logger logger)
{
_httpProvider = httpProvider;
_configService = configService;
_logger = logger;
}
@ -31,7 +28,7 @@ namespace NzbDrone.Core.DataAugmentation.DailySeries
{
try
{
var dailySeriesIds = _httpProvider.DownloadString(_configService.ServiceRootUrl + "/DailySeries/AllIds");
var dailySeriesIds = _httpProvider.DownloadString(Services.RootUrl + "/DailySeries/AllIds");
var seriesIds = Json.Deserialize<List<int>>(dailySeriesIds);
@ -49,7 +46,7 @@ namespace NzbDrone.Core.DataAugmentation.DailySeries
{
try
{
var result = _httpProvider.DownloadString(_configService.ServiceRootUrl + "/DailySeries/Check?seriesId=" + tvdbid);
var result = _httpProvider.DownloadString(Services.RootUrl + "/DailySeries/Check?seriesId=" + tvdbid);
return Convert.ToBoolean(result);
}
catch (Exception ex)

@ -13,19 +13,15 @@ namespace NzbDrone.Core.DataAugmentation.Scene
public class SceneMappingProxy : ISceneMappingProxy
{
private readonly IHttpProvider _httpProvider;
private readonly IConfigService _configService;
public SceneMappingProxy(IHttpProvider httpProvider, IConfigService configService)
public SceneMappingProxy(IHttpProvider httpProvider)
{
_httpProvider = httpProvider;
_configService = configService;
}
public List<SceneMapping> Fetch()
{
var mappingsJson = _httpProvider.DownloadString(_configService.ServiceRootUrl + "/SceneMapping/Active");
var mappingsJson = _httpProvider.DownloadString(Services.RootUrl + "/SceneMapping/Active");
return Json.Deserialize<List<SceneMapping>>(mappingsJson);
}
}

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(16)]
public class updated_imported_history_item : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.Sql(@"UPDATE HISTORY SET Data = replace( Data, '""Path""', '""ImportedPath""' ) WHERE EventType=3");
}
}
}

@ -0,0 +1,15 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(17)]
public class reset_scene_names : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
//we were storing new file name as scene name.
Execute.Sql(@"UPDATE EpisodeFiles SET SceneName = NULL where SceneName != NULL");
}
}
}

@ -62,7 +62,7 @@ namespace NzbDrone.Core.History
{
var history = new History
{
EventType = HistoryEventType.Grabbed,
EventType = HistoryEventType.Grabbed,
Date = DateTime.UtcNow,
Quality = message.Episode.ParsedEpisodeInfo.Quality,
SourceTitle = message.Episode.Report.Title,
@ -81,20 +81,22 @@ namespace NzbDrone.Core.History
public void Handle(EpisodeImportedEvent message)
{
foreach (var episode in message.EpisodeFile.Episodes.Value)
foreach (var episode in message.DroppedEpisode.Episodes)
{
var history = new History
{
EventType = HistoryEventType.DownloadFolderImported,
Date = DateTime.UtcNow,
Quality = message.EpisodeFile.Quality,
SourceTitle = message.EpisodeFile.Path,
SeriesId = message.EpisodeFile.SeriesId,
Quality = message.DroppedEpisode.Quality,
SourceTitle = message.ImportedEpisode.SceneName,
SeriesId = message.ImportedEpisode.SeriesId,
EpisodeId = episode.Id
};
history.Data.Add("Path", message.EpisodeFile.Path);
history.Data.Add("Filename", Path.GetFileNameWithoutExtension(message.EpisodeFile.Path));
//Won't have a value since we publish this event before saving to DB.
//history.Data.Add("FileId", message.ImportedEpisode.Id.ToString());
history.Data.Add("DroppedPath", message.DroppedEpisode.Path);
history.Data.Add("ImportedPath", message.ImportedEpisode.Path);
_historyRepository.Insert(history);
}

@ -4,6 +4,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using NLog;
using NzbDrone.Core.Parser.Model;
@ -12,7 +13,7 @@ namespace NzbDrone.Core.Indexers
{
public interface IParseFeed
{
IEnumerable<ReportInfo> Process(Stream source, string url);
IEnumerable<ReportInfo> Process(string xml, string url);
}
public class BasicRssParser : IParseFeed
@ -24,34 +25,37 @@ namespace NzbDrone.Core.Indexers
_logger = LogManager.GetCurrentClassLogger();
}
public IEnumerable<ReportInfo> Process(Stream source, string url)
public IEnumerable<ReportInfo> Process(string xml, string url)
{
var document = XDocument.Load(source);
var items = document.Descendants("item");
using (var xmlTextReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { ProhibitDtd = false, IgnoreComments = true }))
{
var document = XDocument.Load(xmlTextReader);
var items = document.Descendants("item");
var result = new List<ReportInfo>();
var result = new List<ReportInfo>();
foreach (var item in items)
{
try
foreach (var item in items)
{
var reportInfo = ParseFeedItem(item);
if (reportInfo != null)
try
{
reportInfo.NzbUrl = GetNzbUrl(item);
reportInfo.NzbInfoUrl = GetNzbInfoUrl(item);
result.Add(reportInfo);
var reportInfo = ParseFeedItem(item);
if (reportInfo != null)
{
reportInfo.NzbUrl = GetNzbUrl(item);
reportInfo.NzbInfoUrl = GetNzbInfoUrl(item);
result.Add(reportInfo);
}
}
catch (Exception itemEx)
{
itemEx.Data.Add("Item", item.Title());
_logger.ErrorException("An error occurred while processing feed item from " + url, itemEx);
}
}
catch (Exception itemEx)
{
itemEx.Data.Add("Item", item.Title());
_logger.ErrorException("An error occurred while processing feed item from " + url, itemEx);
}
}
return result;
return result;
}
}

@ -106,19 +106,26 @@ namespace NzbDrone.Core.Indexers
try
{
_logger.Trace("Downloading Feed " + url);
var stream = _httpProvider.DownloadStream(url);
result.AddRange(indexer.Parser.Process(stream, url));
var xml = _httpProvider.DownloadString(url);
if (!string.IsNullOrWhiteSpace(xml))
{
result.AddRange(indexer.Parser.Process(xml, url));
}
else
{
_logger.Warn("{0} returned empty response.", url);
}
}
catch (WebException webException)
{
if (webException.Message.Contains("503") || webException.Message.Contains("timed out"))
if (webException.Message.Contains("502") || webException.Message.Contains("503") || webException.Message.Contains("timed out"))
{
_logger.Warn("{0} server is currently unavailable. {1} {2}", indexer.Name, url, webException.Message);
}
else
{
webException.Data.Add("FeedUrl", url);
_logger.WarnException("An error occurred while processing feed. " + url, webException);
_logger.Warn("{0} {1} {2}", indexer.Name, url, webException.Message);
}
}
catch (Exception feedEx)

@ -18,7 +18,7 @@ namespace NzbDrone.Core.Instrumentation
public void Trim()
{
var trimDate = DateTime.UtcNow.AddDays(-15).Date;
var trimDate = DateTime.UtcNow.AddDays(-7).Date;
Delete(c => c.Time <= trimDate);
}
}

@ -44,30 +44,31 @@ namespace NzbDrone.Core.MediaCover
foreach (var cover in series.Images)
{
var fileName = GetCoverPath(series.Id, cover.CoverType);
if (!_coverExistsSpecification.AlreadyExists(cover.Url, fileName))
try
{
DownloadCover(series, cover);
if (!_coverExistsSpecification.AlreadyExists(cover.Url, fileName))
{
DownloadCover(series, cover);
}
}
catch (WebException e)
{
_logger.Warn(string.Format("Couldn't download media cover for {0}. {1}", series, e.Message));
}
catch (Exception e)
{
_logger.ErrorException("Couldn't download media cover for " + series, e);
}
}
}
private void DownloadCover(Series series, MediaCover cover)
{
try
{
var fileName = GetCoverPath(series.Id, cover.CoverType);
var fileName = GetCoverPath(series.Id, cover.CoverType);
_logger.Info("Downloading {0} for {1} {2}", cover.CoverType, series, cover.Url);
_httpProvider.DownloadFile(cover.Url, fileName);
_logger.Info("Downloading {0} for {1} {2}", cover.CoverType, series, cover.Url);
_httpProvider.DownloadFile(cover.Url, fileName);
}
catch (WebException e)
{
_logger.Warn("Couldn't download media cover for " + series);
}
catch (Exception e)
{
_logger.ErrorException("Couldn't download media cover for " + series, e);
}
}
public void HandleAsync(SeriesDeletedEvent message)

@ -24,9 +24,9 @@ namespace NzbDrone.Core.MediaFiles
private const string EXTENSIONS =
//XBMC
".m4v .3gp .nsv .ts .ty .strm .rm .rmvb .m3u .ifo .mov .qt .divx .xvid .bivx .vob .nrg .img" +
".m4v .3gp .nsv .ts .ty .strm .rm .rmvb .m3u .ifo .mov .qt .divx .xvid .bivx .vob .nrg .img " +
".iso .pva .wmv .asf .asx .ogm .m2v .avi .bin .dat .dvr-ms .mpg .mpeg .mp4 .mkv .avc .vp3 " +
".svq3 .nuv .viv .dv .fli .flv .wpl" +
".svq3 .nuv .viv .dv .fli .flv .wpl " +
//Other
".m2ts";

@ -13,8 +13,8 @@ namespace NzbDrone.Core.MediaFiles
{
public interface IMoveEpisodeFiles
{
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series);
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
string MoveEpisodeFile(EpisodeFile episodeFile, Series series);
string MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
}
public class MoveEpisodeFiles : IMoveEpisodeFiles
@ -38,27 +38,26 @@ namespace NzbDrone.Core.MediaFiles
_logger = logger;
}
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series)
public string MoveEpisodeFile(EpisodeFile episodeFile, Series series)
{
var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id);
var newFileName = _buildFileNames.BuildFilename(episodes, series, episodeFile);
var destinationFilename = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
var filePath = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
MoveFile(episodeFile, filePath);
return MoveFile(episodeFile, destinationFilename);
return filePath;
}
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
public string MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
{
var newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile);
var destinationFilename = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
episodeFile = MoveFile(episodeFile, destinationFilename);
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
MoveFile(episodeFile, filePath);
_messageAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode));
return episodeFile;
return filePath;
}
private EpisodeFile MoveFile(EpisodeFile episodeFile, string destinationFilename)
private void MoveFile(EpisodeFile episodeFile, string destinationFilename)
{
if (!_diskProvider.FileExists(episodeFile.Path))
{
@ -85,10 +84,6 @@ namespace NzbDrone.Core.MediaFiles
_logger.Debug("Unable to apply folder permissions to: ", destinationFilename);
_logger.TraceException(ex.Message, ex);
}
episodeFile.Path = destinationFilename;
return episodeFile;
}
}
}

@ -13,7 +13,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
{
List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownloads = false);
}
public class ImportApprovedEpisodes : IImportApprovedEpisodes
{
private readonly IUpgradeMediaFiles _episodeFileUpgrader;
@ -62,15 +62,17 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path);
episodeFile.Quality = localEpisode.Quality;
episodeFile.SeasonNumber = localEpisode.SeasonNumber;
episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
episodeFile.Episodes = localEpisode.Episodes;
if (newDownload)
{
episodeFile = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode);
_messageAggregator.PublishEvent(new EpisodeImportedEvent(episodeFile));
episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
episodeFile.Path = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode);
_messageAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile));
_messageAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode));
}
_mediaFileService.Add(episodeFile);
imported.Add(importDecision);
}

@ -24,7 +24,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
try
{
var path = Directory.GetParent(localEpisode.Series.Path);
var freeSpace = _diskProvider.GetAvilableSpace(path.FullName);
var freeSpace = _diskProvider.GetAvailableSpace(path.FullName);
if (freeSpace < localEpisode.Size + 100.Megabytes())
{

@ -1,14 +1,17 @@
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.Events
{
public class EpisodeImportedEvent : IEvent
{
public EpisodeFile EpisodeFile { get; private set; }
public LocalEpisode DroppedEpisode { get; private set; }
public EpisodeFile ImportedEpisode { get; private set; }
public EpisodeImportedEvent(EpisodeFile episodeFile)
public EpisodeImportedEvent(LocalEpisode droppedEpisode, EpisodeFile importedEpisode)
{
EpisodeFile = episodeFile;
DroppedEpisode = droppedEpisode;
ImportedEpisode = importedEpisode;
}
}
}

@ -34,14 +34,12 @@ namespace NzbDrone.Core.MediaFiles
{
var renamed = new List<EpisodeFile>();
foreach (var file in episodeFiles)
foreach (var episodeFile in episodeFiles)
{
try
{
var episodeFile = file;
_logger.Trace("Renaming episode file: {0}", episodeFile);
episodeFile = _episodeFileMover.MoveEpisodeFile(episodeFile, series);
episodeFile.Path = _episodeFileMover.MoveEpisodeFile(episodeFile, series);
_mediaFileService.Update(episodeFile);
renamed.Add(episodeFile);
@ -54,7 +52,7 @@ namespace NzbDrone.Core.MediaFiles
}
catch (Exception ex)
{
_logger.ErrorException("Failed to rename file: " + file.Path, ex);
_logger.ErrorException("Failed to rename file: " + episodeFile.Path, ex);
}
}

@ -7,7 +7,7 @@ namespace NzbDrone.Core.MediaFiles
{
public interface IUpgradeMediaFiles
{
EpisodeFile UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
string UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
}
public class UpgradeMediaFileService : IUpgradeMediaFiles
@ -31,7 +31,7 @@ namespace NzbDrone.Core.MediaFiles
_logger = logger;
}
public EpisodeFile UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
public string UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
{
var existingFiles = localEpisode.Episodes
.Where(e => e.EpisodeFileId > 0)

@ -158,6 +158,8 @@
<Compile Include="Datastore\Migration\013_add_air_date_utc.cs" />
<Compile Include="Datastore\Migration\014_drop_air_date.cs" />
<Compile Include="Datastore\Migration\015_add_air_date_as_string.cs" />
<Compile Include="Datastore\Migration\016_updated_imported_history_item.cs" />
<Compile Include="Datastore\Migration\017_reset_scene_names.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />
@ -342,6 +344,7 @@
<Compile Include="Parser\Model\ReportInfo.cs" />
<Compile Include="Parser\Parser.cs" />
<Compile Include="Parser\ParsingService.cs" />
<Compile Include="Parser\QualityParser.cs" />
<Compile Include="Providers\UpdateXemMappingsCommand.cs" />
<Compile Include="Qualities\QualityProfileInUseException.cs" />
<Compile Include="Qualities\QualitySizeRepository.cs" />
@ -350,6 +353,7 @@
<Compile Include="Rest\RestException.cs" />
<Compile Include="SeriesStats\SeriesStatisticsService.cs" />
<Compile Include="Tv\EpisodeService.cs" />
<Compile Include="Tv\Events\EpisodeInfoDeletedEvent.cs" />
<Compile Include="Tv\Events\EpisodeInfoUpdatedEvent.cs" />
<Compile Include="Tv\Events\EpisodeInfoAddedEvent.cs" />
<Compile Include="Tv\Events\SeriesAddedEvent.cs" />
@ -493,6 +497,7 @@
<Compile Include="Tv\RefreshSeriesService.cs" />
<Compile Include="Update\Commands\ApplicationUpdateCommand.cs" />
<Compile Include="Update\InstallUpdateService.cs" />
<Compile Include="Update\UpdatePackageAvailable.cs" />
<Compile Include="Update\UpdatePackageProvider.cs" />
<Compile Include="Update\UpdatePackage.cs" />
<Compile Include="Update\UpdateCheckService.cs" />

@ -17,7 +17,7 @@ namespace NzbDrone.Core.Parser.Model
public bool IsRecentEpisode()
{
return Episodes.Any(e => e.AirDateUtc >= DateTime.Today.AddDays(-14));
return Episodes.Any(e => e.AirDateUtc >= DateTime.UtcNow.Date.AddDays(-14));
}
public override string ToString()

@ -74,7 +74,8 @@ namespace NzbDrone.Core.Parser
private static readonly Regex MultiPartCleanupRegex = new Regex(@"\(\d+\)$", RegexOptions.Compiled);
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<italian>ita|italian)|(?<german>german\b)|(?<flemish>flemish)|(?<greek>greek)(?:\W|_)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<italian>ita|italian)|(?<german>german\b)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)FR)(?:\W|_)",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static ParsedEpisodeInfo ParsePath(string path)
{
@ -119,7 +120,7 @@ namespace NzbDrone.Core.Parser
break;
result.Language = ParseLanguage(title);
result.Quality = ParseQuality(title);
result.Quality = QualityParser.ParseQuality(title);
return result;
}
}
@ -240,155 +241,6 @@ namespace NzbDrone.Core.Parser
return parseResult.SeriesTitle;
}
private static QualityModel ParseQuality(string name)
{
Logger.Trace("Trying to parse quality for {0}", name);
name = name.Trim();
var normalizedName = CleanSeriesTitle(name);
var result = new QualityModel { Quality = Quality.Unknown };
result.Proper = (normalizedName.Contains("proper") || normalizedName.Contains("repack"));
if ((normalizedName.Contains("dvd") && !normalizedName.Contains("avcdvd")) || normalizedName.Contains("bdrip") || normalizedName.Contains("brrip"))
{
result.Quality = Quality.DVD;
return result;
}
if (normalizedName.Contains("xvid") || normalizedName.Contains("divx") || normalizedName.Contains("dsr"))
{
if (normalizedName.Contains("bluray"))
{
result.Quality = Quality.DVD;
return result;
}
result.Quality = Quality.SDTV;
return result;
}
if (normalizedName.Contains("bluray"))
{
if (normalizedName.Contains("720p"))
{
result.Quality = Quality.Bluray720p;
return result;
}
if (normalizedName.Contains("1080p"))
{
result.Quality = Quality.Bluray1080p;
return result;
}
result.Quality = Quality.Bluray720p;
return result;
}
if (normalizedName.Contains("webdl") || normalizedName.Contains("webrip"))
{
if (normalizedName.Contains("1080p"))
{
result.Quality = Quality.WEBDL1080p;
return result;
}
if (normalizedName.Contains("720p"))
{
result.Quality = Quality.WEBDL720p;
return result;
}
if (name.Contains("[WEBDL]"))
{
result.Quality = Quality.WEBDL720p;
return result;
}
result.Quality = Quality.WEBDL480p;
return result;
}
if (normalizedName.Contains("trollhd") || normalizedName.Contains("rawhd"))
{
result.Quality = Quality.RAWHD;
return result;
}
if (normalizedName.Contains("x264") || normalizedName.Contains("h264") || normalizedName.Contains("720p"))
{
if (normalizedName.Contains("1080p"))
{
result.Quality = Quality.HDTV1080p;
return result;
}
result.Quality = Quality.HDTV720p;
return result;
}
//Based on extension
if (result.Quality == Quality.Unknown && !name.ContainsInvalidPathChars())
{
try
{
switch (Path.GetExtension(name).ToLower())
{
case ".avi":
case ".xvid":
case ".divx":
case ".wmv":
case ".mp4":
case ".mpg":
case ".mpeg":
case ".mov":
case ".rm":
case ".rmvb":
case ".flv":
case ".dvr-ms":
case ".ogm":
case ".strm":
{
result.Quality = Quality.SDTV;
break;
}
case ".mkv":
case ".ts":
{
result.Quality = Quality.HDTV720p;
break;
}
}
}
catch (ArgumentException)
{
//Swallow exception for cases where string contains illegal
//path characters.
}
}
if (name.Contains("[HDTV]"))
{
result.Quality = Quality.HDTV720p;
return result;
}
if (normalizedName.Contains("hdtv") && normalizedName.Contains("1080p"))
{
result.Quality = Quality.HDTV1080p;
return result;
}
if ((normalizedName.Contains("sdtv") || normalizedName.Contains("pdtv") ||
(result.Quality == Quality.Unknown && normalizedName.Contains("hdtv"))) &&
!normalizedName.Contains("mpeg"))
{
result.Quality = Quality.SDTV;
return result;
}
return result;
}
private static Language ParseLanguage(string title)
{
var lowerTitle = title.ToLower();
@ -461,6 +313,9 @@ namespace NzbDrone.Core.Parser
if (match.Groups["greek"].Captures.Cast<Capture>().Any())
return Language.Greek;
if (match.Groups["french"].Success)
return Language.French;
return Language.English;
}

@ -0,0 +1,213 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Parser
{
public class QualityParser
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly Regex SourceRegex = new Regex(@"(?<bluray>BluRay)|
(?<webdl>WEB-DL|WEBDL|WEB\sDL|WEB\-DL|WebRip)|
(?<hdtv>HDTV)|
(?<bdrip>BDRiP)|(?<brrip>BRRip)|(?<dvd>\bDVD\b|DVDRip|NTSC|PAL|xvidvd)|
(?<dsr>WS\sDSR|WS_DSR|WS\.DSR|DSR)|(?<pdtv>PDTV)|(?<sdtv>SDTV)",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
private static readonly Regex ResolutionRegex = new Regex(@"(?<_480p>480p)|(?<_720p>720p)|(?<_1080p>1080p)",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex CodecRegex = new Regex(@"(?<x264>x264)|(?<h264>h264)|(?<xvidhd>XvidHD)|(?<xvid>Xvid)|(?<divx>divx)",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static QualityModel ParseQuality(string name)
{
Logger.Trace("Trying to parse quality for {0}", name);
name = name.Trim();
var normalizedName = name.CleanSeriesTitle();
var result = new QualityModel { Quality = Quality.Unknown };
result.Proper = (normalizedName.Contains("proper") || normalizedName.Contains("repack"));
if (normalizedName.Contains("trollhd") || normalizedName.Contains("rawhd"))
{
result.Quality = Quality.RAWHD;
return result;
}
var sourceMatch = SourceRegex.Match(name);
var resolution = ParseResolution(name);
var codecRegex = CodecRegex.Match(name);
if (sourceMatch.Groups["bluray"].Success)
{
if (codecRegex.Groups["xvid"].Success || codecRegex.Groups["divx"].Success)
{
result.Quality = Quality.DVD;
return result;
}
if (resolution == Resolution._1080p)
{
result.Quality = Quality.Bluray1080p;
return result;
}
result.Quality = Quality.Bluray720p;
return result;
}
if (sourceMatch.Groups["webdl"].Success)
{
if (resolution == Resolution._1080p)
{
result.Quality = Quality.WEBDL1080p;
return result;
}
if (resolution == Resolution._720p)
{
result.Quality = Quality.WEBDL720p;
return result;
}
if (name.Contains("[WEBDL]"))
{
result.Quality = Quality.WEBDL720p;
return result;
}
result.Quality = Quality.WEBDL480p;
return result;
}
if (sourceMatch.Groups["hdtv"].Success)
{
if (resolution == Resolution._1080p)
{
result.Quality = Quality.HDTV1080p;
return result;
}
if (resolution == Resolution._720p)
{
result.Quality = Quality.HDTV720p;
return result;
}
if (name.Contains("[HDTV]"))
{
result.Quality = Quality.HDTV720p;
return result;
}
result.Quality = Quality.SDTV;
return result;
}
if (sourceMatch.Groups["dvd"].Success ||
sourceMatch.Groups["bdrip"].Success ||
sourceMatch.Groups["brrip"].Success)
{
result.Quality = Quality.DVD;
return result;
}
if (sourceMatch.Groups["pdtv"].Success ||
sourceMatch.Groups["sdtv"].Success ||
sourceMatch.Groups["dsr"].Success)
{
result.Quality = Quality.SDTV;
return result;
}
if (resolution == Resolution._1080p)
{
result.Quality = Quality.HDTV1080p;
return result;
}
if (resolution == Resolution._720p)
{
result.Quality = Quality.HDTV720p;
return result;
}
if (codecRegex.Groups["x264"].Success)
{
result.Quality = Quality.SDTV;
return result;
}
//Based on extension
if (result.Quality == Quality.Unknown && !name.ContainsInvalidPathChars())
{
try
{
switch (Path.GetExtension(name).ToLower())
{
case ".avi":
case ".xvid":
case ".divx":
case ".wmv":
case ".mp4":
case ".mpg":
case ".mpeg":
case ".mov":
case ".rm":
case ".rmvb":
case ".flv":
case ".dvr-ms":
case ".ogm":
case ".strm":
{
result.Quality = Quality.SDTV;
break;
}
case ".mkv":
case ".ts":
{
result.Quality = Quality.HDTV720p;
break;
}
}
}
catch (ArgumentException)
{
//Swallow exception for cases where string contains illegal
//path characters.
}
}
return result;
}
private static Resolution ParseResolution(string name)
{
var match = ResolutionRegex.Match(name);
if (!match.Success) return Resolution.Unknown;
if (match.Groups["_480p"].Success) return Resolution._480p;
if (match.Groups["_720p"].Success) return Resolution._720p;
if (match.Groups["_1080p"].Success) return Resolution._1080p;
return Resolution.Unknown;
}
}
public enum Resolution
{
_480p,
_720p,
_1080p,
Unknown
}
}

@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
@ -12,31 +14,37 @@ namespace NzbDrone.Core.Providers
{
void UpdateMappings();
void UpdateMappings(int seriesId);
void UpdateMappings(Series series);
void PerformUpdate(Series series);
}
public class XemProvider : IXemProvider, IExecute<UpdateXemMappingsCommand>, IHandle<SeriesUpdatedEvent>
public class XemProvider : IXemProvider, IExecute<UpdateXemMappingsCommand>, IHandle<SeriesUpdatedEvent>, IHandleAsync<ApplicationStartedEvent>
{
private readonly IEpisodeService _episodeService;
private readonly IXemCommunicationProvider _xemCommunicationProvider;
private readonly ISeriesService _seriesService;
private readonly ICached<bool> _cache;
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
public XemProvider(IEpisodeService episodeService, IXemCommunicationProvider xemCommunicationProvider, ISeriesService seriesService)
public XemProvider(IEpisodeService episodeService,
IXemCommunicationProvider xemCommunicationProvider,
ISeriesService seriesService, ICacheManger cacheManger)
{
if (seriesService == null) throw new ArgumentNullException("seriesService");
_episodeService = episodeService;
_xemCommunicationProvider = xemCommunicationProvider;
_seriesService = seriesService;
_cache = cacheManger.GetCache<bool>(GetType());
}
public void UpdateMappings()
{
_logger.Trace("Starting scene numbering update");
try
{
var ids = _xemCommunicationProvider.GetXemSeriesIds();
var ids = GetXemSeriesIds();
var series = _seriesService.GetAllSeries();
var wantedSeries = series.Where(s => ids.Contains(s.TvdbId)).ToList();
@ -64,12 +72,15 @@ namespace NzbDrone.Core.Providers
_logger.Trace("Series could not be found: {0}", seriesId);
return;
}
UpdateMappings(series);
}
var xemIds = _xemCommunicationProvider.GetXemSeriesIds();
if (!xemIds.Contains(series.TvdbId))
public void UpdateMappings(Series series)
{
if (!_cache.Find(series.TvdbId.ToString()))
{
_logger.Trace("Xem doesn't have a mapping for this series: {0}", series.TvdbId);
_logger.Trace("Scene numbering is not available for {0} [{1}]", series.Title, series.TvdbId);
return;
}
@ -125,6 +136,20 @@ namespace NzbDrone.Core.Providers
}
}
private List<int> GetXemSeriesIds()
{
_cache.Clear();
var ids = _xemCommunicationProvider.GetXemSeriesIds();
foreach (var id in ids)
{
_cache.Set(id.ToString(), true);
}
return ids;
}
public void Execute(UpdateXemMappingsCommand message)
{
if (message.SeriesId.HasValue)
@ -139,7 +164,12 @@ namespace NzbDrone.Core.Providers
public void Handle(SeriesUpdatedEvent message)
{
PerformUpdate(message.Series);
UpdateMappings(message.Series);
}
public void HandleAsync(ApplicationStartedEvent message)
{
GetXemSeriesIds();
}
}
}

@ -8,7 +8,7 @@ namespace NzbDrone.Core.RootFolders
{
public string Path { get; set; }
public long FreeSpace { get; set; }
public long? FreeSpace { get; set; }
public List<UnmappedFolder> UnmappedFolders { get; set; }
}

@ -17,7 +17,7 @@ namespace NzbDrone.Core.RootFolders
RootFolder Add(RootFolder rootDir);
void Remove(int id);
List<UnmappedFolder> GetUnmappedFolders(string path);
Dictionary<string, long> FreeSpaceOnDrives();
Dictionary<string, long?> FreeSpaceOnDrives();
RootFolder Get(int id);
}
@ -55,7 +55,7 @@ namespace NzbDrone.Core.RootFolders
{
if (_diskProvider.FolderExists(folder.Path))
{
folder.FreeSpace = _diskProvider.GetAvilableSpace(folder.Path);
folder.FreeSpace = _diskProvider.GetAvailableSpace(folder.Path);
folder.UnmappedFolders = GetUnmappedFolders(folder.Path);
}
});
@ -82,7 +82,7 @@ namespace NzbDrone.Core.RootFolders
_rootFolderRepository.Insert(rootFolder);
rootFolder.FreeSpace = _diskProvider.GetAvilableSpace(rootFolder.Path);
rootFolder.FreeSpace = _diskProvider.GetAvailableSpace(rootFolder.Path);
rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path);
return rootFolder;
}
@ -126,9 +126,9 @@ namespace NzbDrone.Core.RootFolders
return results;
}
public Dictionary<string, long> FreeSpaceOnDrives()
public Dictionary<string, long?> FreeSpaceOnDrives()
{
var freeSpace = new Dictionary<string, long>();
var freeSpace = new Dictionary<string, long?>();
var rootDirs = All();
@ -140,7 +140,7 @@ namespace NzbDrone.Core.RootFolders
{
try
{
freeSpace.Add(pathRoot, _diskProvider.GetAvilableSpace(rootDir.Path));
freeSpace.Add(pathRoot, _diskProvider.GetAvailableSpace(rootDir.Path));
}
catch (Exception ex)
{
@ -155,7 +155,7 @@ namespace NzbDrone.Core.RootFolders
public RootFolder Get(int id)
{
var rootFolder = _rootFolderRepository.Get(id);
rootFolder.FreeSpace = _diskProvider.GetAvilableSpace(rootFolder.Path);
rootFolder.FreeSpace = _diskProvider.GetAvailableSpace(rootFolder.Path);
rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path);
return rootFolder;
}

@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class EpisodeInfoDeletedEvent : IEvent
{
public ReadOnlyCollection<Episode> Episodes { get; private set; }
public EpisodeInfoDeletedEvent(IList<Episode> episodes)
{
Episodes = new ReadOnlyCollection<Episode>(episodes);
}
}
}

@ -34,7 +34,7 @@ namespace NzbDrone.Core.Tv
var successCount = 0;
var failCount = 0;
var existinEpisodes = _episodeService.GetEpisodeBySeries(series.Id);
var existingEpisodes = _episodeService.GetEpisodeBySeries(series.Id);
var seasons = _seasonService.GetSeasonsBySeries(series.Id);
var updateList = new List<Episode>();
@ -44,11 +44,11 @@ namespace NzbDrone.Core.Tv
{
try
{
var episodeToUpdate = existinEpisodes.SingleOrDefault(e => e.SeasonNumber == episode.SeasonNumber && e.EpisodeNumber == episode.EpisodeNumber);
var episodeToUpdate = existingEpisodes.SingleOrDefault(e => e.SeasonNumber == episode.SeasonNumber && e.EpisodeNumber == episode.EpisodeNumber);
if (episodeToUpdate != null)
{
existinEpisodes.Remove(episodeToUpdate);
existingEpisodes.Remove(episodeToUpdate);
updateList.Add(episodeToUpdate);
}
else
@ -82,11 +82,10 @@ namespace NzbDrone.Core.Tv
AdjustMultiEpisodeAirTime(series, allEpisodes);
_episodeService.DeleteMany(existinEpisodes);
_episodeService.DeleteMany(existingEpisodes);
_episodeService.UpdateMany(updateList);
_episodeService.InsertMany(newList);
if (newList.Any())
{
_messageAggregator.PublishEvent(new EpisodeInfoAddedEvent(newList, series));
@ -97,6 +96,11 @@ namespace NzbDrone.Core.Tv
_messageAggregator.PublishEvent(new EpisodeInfoUpdatedEvent(updateList));
}
if (existingEpisodes.Any())
{
_messageAggregator.PublishEvent(new EpisodeInfoDeletedEvent(updateList));
}
if (failCount != 0)
{
_logger.Info("Finished episode refresh for series: {0}. Successful: {1} - Failed: {2} ",

Binary file not shown.

@ -26,7 +26,7 @@ namespace NzbDrone.Core.Update
{
var latestAvailable = _updatePackageProvider.GetLatestUpdate();
if (latestAvailable == null || latestAvailable.Version <= BuildInfo.Version)
if (latestAvailable == null)
{
_logger.Debug("No update available.");
return null;

@ -1,11 +1,18 @@
using System;
using Newtonsoft.Json;
namespace NzbDrone.Core.Update
{
public class UpdatePackage
{
public string Url { get; set; }
public string FileName { get; set; }
public String Id { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
public Version Version { get; set; }
public String Branch { get; set; }
public DateTime ReleaseDate { get; set; }
public String FileName { get; set; }
public String Url { get; set; }
}
}

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Update
{
public class UpdatePackageAvailable
{
public Boolean Available { get; set; }
public UpdatePackage UpdatePackage { get; set; }
}
}

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
@ -11,56 +12,30 @@ namespace NzbDrone.Core.Update
{
public interface IUpdatePackageProvider
{
IEnumerable<UpdatePackage> GetAvailablePackages();
UpdatePackage GetLatestUpdate();
}
public class UpdatePackageProvider : IUpdatePackageProvider
{
private readonly IConfigFileProvider _configService;
private readonly IConfigFileProvider _configFileProvider;
private readonly IHttpProvider _httpProvider;
private readonly Logger _logger;
private static readonly Regex ParseRegex = new Regex(@"(?:\>)(?<filename>NzbDrone.+?(?<version>(?<=\.)\d+\.\d+\.\d+\.\d+).+?)(?:\<\/a\>)",
RegexOptions.IgnoreCase);
public UpdatePackageProvider(IConfigFileProvider configService, IHttpProvider httpProvider, Logger logger)
public UpdatePackageProvider(IConfigFileProvider configFileProvider, IHttpProvider httpProvider, Logger logger)
{
_configService = configService;
_configFileProvider = configFileProvider;
_httpProvider = httpProvider;
_logger = logger;
}
public IEnumerable<UpdatePackage> GetAvailablePackages()
public UpdatePackage GetLatestUpdate()
{
var updateList = new List<UpdatePackage>();
var branch = _configService.Branch;
var version = BuildInfo.Version;
var updateUrl = String.Format("http://update.nzbdrone.com/v{0}/{1}/", version.Major, branch);
_logger.Debug("Getting a list of updates from {0}", updateUrl);
var rawUpdateList = _httpProvider.DownloadString(updateUrl);
var matches = ParseRegex.Matches(rawUpdateList);
var url = String.Format("{0}/v1/update/{1}?version={2}", Services.RootUrl, _configFileProvider.Branch, BuildInfo.Version);
var update = JsonConvert.DeserializeObject<UpdatePackageAvailable>(_httpProvider.DownloadString(url));
foreach (Match match in matches)
{
var updatePackage = new UpdatePackage();
updatePackage.FileName = match.Groups["filename"].Value;
updatePackage.Url = updateUrl + updatePackage.FileName;
updatePackage.Version = new Version(match.Groups["version"].Value);
updateList.Add(updatePackage);
}
if (!update.Available) return null;
_logger.Debug("Found {0} update packages", updateUrl.Length);
return updateList;
}
public UpdatePackage GetLatestUpdate()
{
return GetAvailablePackages().OrderByDescending(c => c.Version).FirstOrDefault();
return update.UpdatePackage;
}
}
}

@ -23,6 +23,7 @@ namespace NzbDrone.Core.Validation
public static IRuleBuilderOptions<T, string> ValidRootUrl<T>(this IRuleBuilder<T, string> ruleBuilder)
{
ruleBuilder.SetValidator(new NotEmptyValidator(null));
return ruleBuilder.SetValidator(new RegularExpressionValidator("^http(?:s)?://[a-z0-9-.]+", RegexOptions.IgnoreCase)).WithMessage("must be valid URL that");
}
}

@ -1,6 +1,7 @@
using System;
using NetFwTypeLib;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Host.AccessControl
@ -31,9 +32,6 @@ namespace NzbDrone.Host.AccessControl
return;
}
CloseFirewallPort();
//Open the new port
OpenFirewallPort(_configFileProvider.Port);
}
}
@ -91,38 +89,10 @@ namespace NzbDrone.Host.AccessControl
}
}
private void CloseFirewallPort()
{
try
{
var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false);
var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType);
var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts;
var portNumber = 8989;
foreach (INetFwOpenPort p in ports)
{
if (p.Name == "NzbDrone")
{
portNumber = p.Port;
break;
}
}
if (portNumber != _configFileProvider.Port)
{
ports.Remove(portNumber, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP);
}
}
catch (Exception ex)
{
_logger.WarnException("Failed to close port in firewall for NzbDrone", ex);
}
}
private bool IsFirewallEnabled()
{
if (OsInfo.IsLinux) return false;
try
{
var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false);
@ -135,7 +105,5 @@ namespace NzbDrone.Host.AccessControl
return false;
}
}
}
}

@ -9,6 +9,7 @@ namespace NzbDrone.Host.AccessControl
public interface IUrlAclAdapter
{
void RefreshRegistration();
string UrlAcl { get; }
}
public class UrlAclAdapter : IUrlAclAdapter
@ -24,18 +25,25 @@ namespace NzbDrone.Host.AccessControl
_logger = logger;
}
public string UrlAcl
{
get
{
return "http://*:" + _configFileProvider.Port + "/";
}
}
public void RefreshRegistration()
{
if (OsInfo.Version.Major < 6)
return;
RegisterUrl(_configFileProvider.Port);
RegisterUrl();
}
private void RegisterUrl(int portNumber)
private void RegisterUrl()
{
var arguments = String.Format("http add urlacl http://*:{0}/ sddl=D:(A;;GX;;;S-1-1-0)", portNumber);
var arguments = String.Format("http add urlacl {0} sddl=D:(A;;GX;;;S-1-1-0)", UrlAcl);
RunNetsh(arguments);
}

@ -4,7 +4,6 @@ using NLog;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration;
using NzbDrone.Host.AccessControl;
using NzbDrone.Host.Owin;
namespace NzbDrone.Host
@ -23,13 +22,10 @@ namespace NzbDrone.Host
private readonly IProcessProvider _processProvider;
private readonly PriorityMonitor _priorityMonitor;
private readonly IStartupArguments _startupArguments;
private readonly IFirewallAdapter _firewallAdapter;
private readonly IUrlAclAdapter _urlAclAdapter;
private readonly Logger _logger;
public NzbDroneServiceFactory(IConfigFileProvider configFileProvider, IHostController hostController, IRuntimeInfo runtimeInfo,
IProcessProvider processProvider, PriorityMonitor priorityMonitor, IStartupArguments startupArguments,
IFirewallAdapter firewallAdapter, IUrlAclAdapter urlAclAdapter, Logger logger)
IProcessProvider processProvider, PriorityMonitor priorityMonitor, IStartupArguments startupArguments, Logger logger)
{
_configFileProvider = configFileProvider;
_hostController = hostController;
@ -37,8 +33,6 @@ namespace NzbDrone.Host
_processProvider = processProvider;
_priorityMonitor = priorityMonitor;
_startupArguments = startupArguments;
_firewallAdapter = firewallAdapter;
_urlAclAdapter = urlAclAdapter;
_logger = logger;
}
@ -49,11 +43,6 @@ namespace NzbDrone.Host
public void Start()
{
if (OsInfo.IsWindows && _runtimeInfo.IsAdmin)
{
_urlAclAdapter.RefreshRegistration();
_firewallAdapter.MakeAccessible();
}
_hostController.StartServer();
if (!_startupArguments.Flags.Contains(StartupArguments.NO_BROWSER) &&

@ -1,4 +1,5 @@
using Nancy.Bootstrapper;
using System;
using Nancy.Bootstrapper;
using NzbDrone.Api;
using NzbDrone.Api.SignalR;
using NzbDrone.Common.Composition;
@ -6,6 +7,7 @@ using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.RootFolders;
using NzbDrone.Host.Owin;
namespace NzbDrone.Host
{

@ -122,6 +122,9 @@
<Compile Include="AccessControl\FirewallAdapter.cs" />
<Compile Include="AccessControl\UrlAclAdapter.cs" />
<Compile Include="IUserAlert.cs" />
<Compile Include="Owin\NlogTextWriter.cs" />
<Compile Include="Owin\OwinServiceProvider.cs" />
<Compile Include="Owin\OwinTraceOutputFactory.cs" />
<Compile Include="PlatformValidation.cs" />
<Compile Include="MainAppContainerBuilder.cs" />
<Compile Include="ApplicationModes.cs" />

@ -4,7 +4,6 @@
{
string AppUrl { get; }
void StartServer();
void RestartServer();
void StopServer();
}
}

@ -0,0 +1,41 @@
using System.IO;
using System.Text;
using NLog;
namespace NzbDrone.Host.Owin
{
public class NlogTextWriter : TextWriter
{
private readonly Logger logger = LogManager.GetCurrentClassLogger();
public override Encoding Encoding
{
get
{
return Encoding.Default;
}
}
public override void Write(char value)
{
logger.Trace(value);
}
public override void Write(char[] buffer)
{
logger.Trace(buffer);
}
public override void Write(string value)
{
logger.Trace(value);
}
public override void Write(char[] buffer, int index, int count)
{
logger.Trace(buffer);
}
}
}

@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.Owin.Hosting;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Security;
using NzbDrone.Core.Configuration;
using NzbDrone.Host.AccessControl;
using NzbDrone.Host.Owin.MiddleWare;
using Owin;
@ -14,13 +16,20 @@ namespace NzbDrone.Host.Owin
{
private readonly IConfigFileProvider _configFileProvider;
private readonly IEnumerable<IOwinMiddleWare> _owinMiddleWares;
private readonly IRuntimeInfo _runtimeInfo;
private readonly IUrlAclAdapter _urlAclAdapter;
private readonly IFirewallAdapter _firewallAdapter;
private readonly Logger _logger;
private IDisposable _host;
public OwinHostController(IConfigFileProvider configFileProvider, IEnumerable<IOwinMiddleWare> owinMiddleWares, Logger logger)
public OwinHostController(IConfigFileProvider configFileProvider, IEnumerable<IOwinMiddleWare> owinMiddleWares,
IRuntimeInfo runtimeInfo, IUrlAclAdapter urlAclAdapter, IFirewallAdapter firewallAdapter, Logger logger)
{
_configFileProvider = configFileProvider;
_owinMiddleWares = owinMiddleWares;
_runtimeInfo = runtimeInfo;
_urlAclAdapter = urlAclAdapter;
_firewallAdapter = firewallAdapter;
_logger = logger;
}
@ -28,16 +37,20 @@ namespace NzbDrone.Host.Owin
{
IgnoreCertErrorPolicy.Register();
var url = "http://*:" + _configFileProvider.Port;
if (OsInfo.IsWindows && _runtimeInfo.IsAdmin)
{
_urlAclAdapter.RefreshRegistration();
_firewallAdapter.MakeAccessible();
}
var options = new StartOptions(url)
var options = new StartOptions(_urlAclAdapter.UrlAcl)
{
ServerFactory = "Microsoft.Owin.Host.HttpListener"
};
_logger.Info("starting server on {0}", url);
_logger.Info("starting server on {0}", _urlAclAdapter.UrlAcl);
_host = WebApp.Start(options, BuildApp);
_host = WebApp.Start(OwinServiceProviderFactory.Create(), options, BuildApp);
}
private void BuildApp(IAppBuilder appBuilder)
@ -56,14 +69,6 @@ namespace NzbDrone.Host.Owin
get { return string.Format("http://localhost:{0}", _configFileProvider.Port); }
}
public void RestartServer()
{
_logger.Warn("Attempting to restart server.");
StopServer();
StartServer();
}
public void StopServer()
{
if (_host == null) return;

@ -0,0 +1,16 @@
using Microsoft.Owin.Hosting.Services;
using Microsoft.Owin.Hosting.Tracing;
namespace NzbDrone.Host.Owin
{
public static class OwinServiceProviderFactory
{
public static ServiceProvider Create()
{
var provider = (ServiceProvider)ServicesFactory.Create();
provider.Add(typeof(ITraceOutputFactory), typeof(OwinTraceOutputFactory));
return provider;
}
}
}

@ -0,0 +1,14 @@
using System.IO;
using Microsoft.Owin.Hosting.Tracing;
namespace NzbDrone.Host.Owin
{
public class OwinTraceOutputFactory : ITraceOutputFactory
{
public TextWriter Create(string outputFile)
{
return new NlogTextWriter();
}
}
}

@ -4,6 +4,7 @@ using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Api.Series;
using System.Linq;
using NzbDrone.Test.Common;
namespace NzbDrone.Integration.Test
{
@ -15,7 +16,7 @@ namespace NzbDrone.Integration.Test
var series = Series.Lookup("archer").First();
series.QualityProfileId = 1;
series.Path = @"C:\Test\Archer";
series.Path = @"C:\Test\Archer".AsOsAgnostic();
series = Series.Post(series);
@ -57,4 +58,4 @@ namespace NzbDrone.Integration.Test
Episodes.Put(updatedEpisode).Monitored.Should().BeFalse();
}
}
}
}

@ -1,4 +1,5 @@
using NLog;
using System.Runtime.CompilerServices;
using NLog;
using NLog.Config;
using NLog.Targets;
using NUnit.Framework;
@ -39,6 +40,7 @@ namespace NzbDrone.Integration.Test
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, consoleTarget));
}
//[TestFixtureSetUp]
[SetUp]
public void SmokeTestSetup()
{
@ -63,6 +65,7 @@ namespace NzbDrone.Integration.Test
NamingConfig = new ClientBase<NamingConfigResource>(RestClient, "config/naming");
}
//[TestFixtureTearDown]
[TearDown]
public void SmokeTestTearDown()
{

@ -33,8 +33,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="FluentAssertions">
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
<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="FluentValidation, Version=4.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>

@ -53,11 +53,17 @@ namespace NzbDrone.Integration.Test
Assert.Fail("Process has exited");
}
if (_restClient.Get(new RestRequest("system/status")).ResponseStatus == ResponseStatus.Completed)
var statusCall = _restClient.Get(new RestRequest("system/status"));
if (statusCall.ResponseStatus == ResponseStatus.Completed)
{
Console.WriteLine("NzbDrone is started. Running Tests");
return;
}
Console.WriteLine("Waiting for NzbDrone to start. Response Status : {0} [{1}] {2}", statusCall.ResponseStatus, statusCall.StatusDescription, statusCall.ErrorException);
Thread.Sleep(500);
}
}

@ -58,5 +58,17 @@ namespace NzbDrone.Integration.Test
RootFolders.All().Should().BeEmpty();
}
[Test]
public void invalid_path_should_return_bad_request()
{
var rootFolder = new RootFolderResource
{
Path = "invalid_path"
};
var postResponse = RootFolders.InvalidPost(rootFolder);
postResponse.Should().NotBeEmpty();
}
}
}

@ -4,6 +4,7 @@ using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Api.Series;
using System.Linq;
using NzbDrone.Test.Common;
namespace NzbDrone.Integration.Test
{
@ -15,7 +16,7 @@ namespace NzbDrone.Integration.Test
var series = Series.Lookup("archer").First();
series.QualityProfileId = 1;
series.Path = @"C:\Test\Archer";
series.Path = @"C:\Test\Archer".AsOsAgnostic();
series = Series.Post(series);
@ -57,4 +58,4 @@ namespace NzbDrone.Integration.Test
Seasons.Put(updatedSeason).Monitored.Should().BeFalse();
}
}
}
}

@ -3,18 +3,13 @@ using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Api.Series;
using System.Linq;
using NzbDrone.Test.Common;
namespace NzbDrone.Integration.Test
{
[TestFixture]
public class SeriesIntegrationTest : IntegrationTest
{
[Test]
public void should_have_no_series_on_start_application()
{
Series.All().Should().BeEmpty();
}
[Test]
public void series_lookup_on_trakt()
{
@ -37,7 +32,7 @@ namespace NzbDrone.Integration.Test
var series = Series.Lookup("archer").First();
series.QualityProfileId = 1;
series.Path = @"C:\Test\Archer";
series.Path = @"C:\Test\Archer".AsOsAgnostic();
series = Series.Post(series);
@ -57,7 +52,7 @@ namespace NzbDrone.Integration.Test
var series = Series.Lookup("90210").First();
series.QualityProfileId = 1;
series.Path = @"C:\Test\90210";
series.Path = @"C:\Test\90210".AsOsAgnostic();
series = Series.Post(series);

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="2.0.1" targetFramework="net40" />
<package id="FluentAssertions" version="2.1.0.0" targetFramework="net40" />
<package id="FluentValidation" version="4.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" />

@ -34,8 +34,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="FluentAssertions">
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
<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="Microsoft.Practices.ServiceLocation">
<HintPath>..\packages\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll</HintPath>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonServiceLocator" version="1.0" />
<package id="FluentAssertions" version="2.0.1" targetFramework="net40" />
<package id="FluentAssertions" version="2.1.0.0" targetFramework="net40" />
<package id="Moq" version="4.0.10827" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" />

@ -43,9 +43,9 @@
<Reference Include="FizzWare.NBuilder, Version=3.0.1.0, Culture=neutral, PublicKeyToken=5651b03e12e42c12, processorArchitecture=MSIL">
<HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference>
<Reference Include="FluentAssertions, Version=2.0.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<Reference Include="FluentAssertions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
<HintPath>..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll</HintPath>
</Reference>
<Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="2.0.1" targetFramework="net40" />
<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" />

@ -83,6 +83,10 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>

@ -1,5 +1,6 @@
using System;
using System.Windows.Forms;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Host;
using NzbDrone.SysTray;
@ -8,6 +9,7 @@ namespace NzbDrone
{
public static class WindowsApp
{
public static void Main(string[] args)
{
try

@ -5,5 +5,6 @@
<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="Owin" version="1.0" targetFramework="net40" />
</packages>

@ -1,4 +1,4 @@
'use strict';
'use strict';
define(
[
'backbone',

@ -7,10 +7,11 @@ define(
'AddSeries/RootFolders/Collection',
'AddSeries/RootFolders/Model',
'Shared/LoadingView',
'Mixins/AsValidatedView',
'Mixins/AutoComplete'
], function (Marionette, RootFolderCollectionView, RootFolderCollection, RootFolderModel, LoadingView) {
], function (Marionette, RootFolderCollectionView, RootFolderCollection, RootFolderModel, LoadingView, AsValidatedView) {
return Marionette.Layout.extend({
var layout = Marionette.Layout.extend({
template: 'AddSeries/RootFolders/LayoutTemplate',
ui: {
@ -55,12 +56,16 @@ define(
Path: this.ui.pathInput.val()
});
RootFolderCollection.add(newDir);
this.bindToModelValidation(newDir);
newDir.save().done(function () {
RootFolderCollection.add(newDir);
self.trigger('folderSelected', {model: newDir});
});
}
});
return AsValidatedView.apply(layout);
});

@ -3,10 +3,13 @@
<h3>Select Folder</h3>
</div>
<div class="modal-body root-folders-modal">
<div class="input-prepend input-append x-path">
<div class="validation-errors"></div>
<div class="input-prepend input-append x-path control-group">
<span class="add-on">&nbsp;<i class="icon-folder-open"></i></span>
<input class="span9" type="text" placeholder="Start Typing Folder Path...">
<button class="btn btn-success x-add"><i class="icon-ok"/></button>
<input class="span9" type="text" validation-name="path" placeholder="Start Typing Folder Path...">
<button class="btn btn-success x-add">
<i class="icon-ok"/>
</button>
</div>
{{#if items}}
<h4>Recent Folders</h4>

@ -4,6 +4,7 @@ define(
'backbone'
], function (Backbone) {
return Backbone.Model.extend({
urlRoot : window.ApiRoot + '/rootfolder',
defaults: {
freeSpace: 0
}

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

Loading…
Cancel
Save