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); SetHttpInfo(exceptionData, report);
SetEnviromentInfo(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) catch (Exception e)
{ {

@ -36,9 +36,9 @@
<Reference Include="FizzWare.NBuilder"> <Reference Include="FizzWare.NBuilder">
<HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath> <HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference> </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> <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>
<Reference Include="Moq"> <Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>

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

@ -25,7 +25,7 @@ namespace NzbDrone.Api.Frontend.Mappers
public override bool CanHandle(string resourceUrl) 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="System\SystemModule.cs" />
<Compile Include="TinyIoCNancyBootstrapper.cs" /> <Compile Include="TinyIoCNancyBootstrapper.cs" />
<Compile Include="Update\UpdateModule.cs" /> <Compile Include="Update\UpdateModule.cs" />
<Compile Include="Validation\PathValidator.cs" />
<Compile Include="Validation\RuleBuilderExtensions.cs" /> <Compile Include="Validation\RuleBuilderExtensions.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

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

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

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

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json;
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Core.Update; using NzbDrone.Core.Update;
using NzbDrone.Api.Mapping; using NzbDrone.Api.Mapping;
@ -32,7 +33,13 @@ namespace NzbDrone.Api.Update
public class UpdateResource : RestResource public class UpdateResource : RestResource
{ {
public String Id { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
public Version Version { get; set; } public Version Version { get; set; }
public String Branch { get; set; }
public DateTime ReleaseDate { get; set; }
public String FileName { get; set; } public String FileName { get; set; }
public String Url { 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;
using FluentValidation.Validators; 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://"); 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"> <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> <HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference> </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> <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>
<Reference Include="Moq"> <Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <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="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1.1" /> <package id="NBuilder" version="3.0.1.1" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" /> <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.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
@ -9,32 +6,38 @@ using NzbDrone.Test.Common;
namespace NzbDrone.Common.Test.DiskProviderTests namespace NzbDrone.Common.Test.DiskProviderTests
{ {
[TestFixture] [TestFixture]
public class IsParentFixture : TestBase<DiskProvider> public class FreeSpaceFixture : TestBase<DiskProvider>
{ {
private string _parent = @"C:\Test".AsOsAgnostic();
[Test] [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] [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] [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> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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> <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>
<Reference Include="Moq"> <Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
@ -62,6 +62,7 @@
<Compile Include="CacheTests\CachedManagerFixture.cs" /> <Compile Include="CacheTests\CachedManagerFixture.cs" />
<Compile Include="CacheTests\CachedFixture.cs" /> <Compile Include="CacheTests\CachedFixture.cs" />
<Compile Include="ConfigFileProviderTest.cs" /> <Compile Include="ConfigFileProviderTest.cs" />
<Compile Include="DiskProviderTests\FreeSpaceFixture.cs" />
<Compile Include="DiskProviderTests\IsParentFixture.cs" /> <Compile Include="DiskProviderTests\IsParentFixture.cs" />
<Compile Include="EnsureTest\PathExtensionFixture.cs" /> <Compile Include="EnsureTest\PathExtensionFixture.cs" />
<Compile Include="EnvironmentTests\StartupArgumentsFixture.cs" /> <Compile Include="EnvironmentTests\StartupArgumentsFixture.cs" />

@ -102,30 +102,31 @@ namespace NzbDrone.Common.Test
} }
[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(); WindowsOnly();
Assert.Throws<DirectoryNotFoundException>(() => "C:\\InValidFolder\\invalidfile.exe".GetActualCasing()); "C:\\WINDOWS\\invalidfile.exe".GetActualCasing().Should().Be("C:\\Windows\\invalidfile.exe");
} }
[Test] [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(); WindowsOnly();
var path = Process.GetCurrentProcess().MainModule.FileName; "C:\\WINDOWS\\SYSTEM32\\FAKEFOLDER\\invalidfile.exe".GetActualCasing().Should().Be("C:\\Windows\\System32\\FAKEFOLDER\\invalidfile.exe");
path.ToUpper().GetActualCasing().Should().Be(path);
path.ToLower().GetActualCasing().Should().Be(path);
} }
[Test] [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; var path = Process.GetCurrentProcess().MainModule.FileName;
path.GetActualCasing().Should().Be(path); path.ToUpper().GetActualCasing().Should().Be(path);
path.GetActualCasing().Should().Be(path); path.ToLower().GetActualCasing().Should().Be(path);
} }
[Test] [Test]
public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windows() 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"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <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="Moq" version="4.0.10827" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" /> <package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" /> <package id="NUnit" version="2.6.2" targetFramework="net40" />

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

@ -95,8 +95,7 @@ namespace NzbDrone.Common.EnsureThat
return param; return param;
} }
private static readonly Regex windowsInvalidPathRegex = new Regex(@"[/*<>""|]", RegexOptions.Compiled);
private static readonly Regex windowsPathRegex = new Regex(@"^[a-zA-Z]:\\", RegexOptions.Compiled);
[DebuggerStepThrough] [DebuggerStepThrough]
public static Param<string> IsValidPath(this Param<string> param) public static Param<string> IsValidPath(this Param<string> param)
@ -104,31 +103,14 @@ namespace NzbDrone.Common.EnsureThat
if (string.IsNullOrWhiteSpace(param.Value)) if (string.IsNullOrWhiteSpace(param.Value))
throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNullOrWhiteSpace); throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNullOrWhiteSpace);
if (param.Value.IsPathValid()) return param;
if (OsInfo.IsLinux) 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));
{
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 Windows path. paths must be a full path eg. C:\\Windows", param.Value));
return param;
} }
} }
} }

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

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Text.RegularExpressions;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -45,6 +46,29 @@ namespace NzbDrone.Common
return String.Equals(firstPath.CleanFilePath(), secondPath.CleanFilePath(), StringComparison.InvariantCultureIgnoreCase); 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) public static bool ContainsInvalidPathChars(this string text)
{ {
return text.IndexOfAny(Path.GetInvalidPathChars()) >= 0; return text.IndexOfAny(Path.GetInvalidPathChars()) >= 0;
@ -58,28 +82,40 @@ namespace NzbDrone.Common
//Drive letter //Drive letter
return dirInfo.Name.ToUpper(); 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) public static string GetActualCasing(this string path)
{ {
var attributes = File.GetAttributes(path);
if (OsInfo.IsLinux || path.StartsWith("\\")) if (OsInfo.IsLinux || path.StartsWith("\\"))
{ {
return path; return path;
} }
if ((attributes & FileAttributes.Directory) == FileAttributes.Directory) if (Directory.Exists(path) && (File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory)
{ {
return GetProperCapitalization(new DirectoryInfo(path)); return GetProperCapitalization(new DirectoryInfo(path));
} }
var fileInfo = new FileInfo(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), fileName);
return Path.Combine(GetProperCapitalization(dirInfo), dirInfo.GetFiles(fileInfo.Name)[0].Name);
} }
public static string GetAppDataPath(this IAppFolderInfo appFolderInfo) public static string GetAppDataPath(this IAppFolderInfo appFolderInfo)

@ -40,7 +40,6 @@ namespace NzbDrone.Common.Serializer
return JsonConvert.SerializeObject(obj); return JsonConvert.SerializeObject(obj);
} }
public static void Serialize<TModel>(TModel model, TextWriter outputStream) public static void Serialize<TModel>(TModel model, TextWriter outputStream)
{ {
var jsonTextWriter = new JsonTextWriter(outputStream); var jsonTextWriter = new JsonTextWriter(outputStream);
@ -52,7 +51,5 @@ namespace NzbDrone.Common.Serializer
{ {
Serialize(model, new StreamWriter(outputStream)); 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;
using System.Threading; using System.Threading;
using NLog;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Host; using NzbDrone.Host;
@ -18,7 +19,6 @@ namespace NzbDrone.Console
} }
catch (Exception e) catch (Exception e)
{ {
System.Console.WriteLine(e.ToString());
System.Console.ReadLine(); 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"; 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] [Test]
public void fetch_should_return_list_of_mappings() public void fetch_should_return_list_of_mappings()
{ {

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

@ -54,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests
private void GivenFreeSpace(long size) private void GivenFreeSpace(long size)
{ {
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(It.IsAny<String>())) .Setup(s => s.GetAvailableSpace(It.IsAny<String>()))
.Returns(size); .Returns(size);
} }
@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
Mocker.GetMock<IDiskProvider>() 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>() Mocker.GetMock<IUpgradeMediaFiles>()
.Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>())) .Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>()));
.Returns(new EpisodeFile());
} }
[Test] [Test]

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

@ -43,9 +43,9 @@
<Reference Include="FizzWare.NBuilder, Version=3.0.1.0, Culture=neutral, PublicKeyToken=5651b03e12e42c12, processorArchitecture=MSIL"> <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> <HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference> </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> <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>
<Reference Include="FluentMigrator, Version=1.1.1.0, Culture=neutral, PublicKeyToken=aacfc7de5acabf05, processorArchitecture=MSIL"> <Reference Include="FluentMigrator, Version=1.1.1.0, Culture=neutral, PublicKeyToken=aacfc7de5acabf05, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
@ -186,6 +186,7 @@
<Compile Include="ProviderTests\DiskProviderTests\ArchiveProviderFixture.cs" /> <Compile Include="ProviderTests\DiskProviderTests\ArchiveProviderFixture.cs" />
<Compile Include="MediaFileTests\DropFolderImportServiceFixture.cs" /> <Compile Include="MediaFileTests\DropFolderImportServiceFixture.cs" />
<Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" /> <Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" />
<Compile Include="TvTests\SeasonServiceTests\HandleEpisodeInfoDeletedEventFixture.cs" />
<Compile Include="TvTests\SeasonServiceTests\SetSeasonPassFixture.cs" /> <Compile Include="TvTests\SeasonServiceTests\SetSeasonPassFixture.cs" />
<Compile Include="TvTests\SeasonServiceTests\SetMonitoredFixture.cs" /> <Compile Include="TvTests\SeasonServiceTests\SetMonitoredFixture.cs" />
<Compile Include="TvTests\SeriesRepositoryTests\QualityProfileRepositoryFixture.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("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("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("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) public void parse_language(string postTitle, Language language)
{ {
var result = Parser.Parser.ParseTitle(postTitle); var result = Parser.Parser.ParseTitle(postTitle);

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

@ -14,7 +14,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests
[Test] [Test]
public void should_return_free_disk_space() 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); result.Should().BeGreaterThan(0);
} }
@ -23,7 +23,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests
{ {
WindowsOnly(); WindowsOnly();
var result = Subject.GetAvilableSpace(@"\\localhost\c$\Windows"); var result = Subject.GetAvailableSpace(@"\\localhost\c$\Windows");
result.Should().BeGreaterThan(0); result.Should().BeGreaterThan(0);
} }
@ -32,13 +32,13 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests
{ {
WindowsOnly(); 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] [Test]
public void should_be_able_to_get_space_on_folder_that_doesnt_exist() 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); result.Should().BeGreaterThan(0);
} }
} }

@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
.Returns(@"C:\"); .Returns(@"C:\");
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(@"C:\")) .Setup(s => s.GetAvailableSpace(@"C:\"))
.Returns(123456); .Returns(123456);
var result = Subject.FreeSpaceOnDrives(); var result = Subject.FreeSpaceOnDrives();
@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
.Returns(@"C:\"); .Returns(@"C:\");
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(@"C:\")) .Setup(s => s.GetAvailableSpace(@"C:\"))
.Returns(123456); .Returns(123456);
var result = Subject.FreeSpaceOnDrives(); var result = Subject.FreeSpaceOnDrives();
@ -76,7 +76,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
.Returns(@"D:\"); .Returns(@"D:\");
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(It.IsAny<string>())) .Setup(s => s.GetAvailableSpace(It.IsAny<string>()))
.Returns(123456); .Returns(123456);
var result = Subject.FreeSpaceOnDrives(); var result = Subject.FreeSpaceOnDrives();
@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.RootFolderTests
.Returns(@"C:\"); .Returns(@"C:\");
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetAvilableSpace(It.IsAny<string>())) .Setup(s => s.GetAvailableSpace(It.IsAny<string>()))
.Throws(new DirectoryNotFoundException()); .Throws(new DirectoryNotFoundException());
var result = Subject.FreeSpaceOnDrives(); 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"); Mocker.GetMock<IConfigFileProvider>().SetupGet(c => c.Branch).Returns("master");
var updates = Subject.GetAvailablePackages().ToList(); Subject.GetLatestUpdate().Should().BeNull();
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);
} }
} }
} }

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

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

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

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

@ -13,19 +13,15 @@ namespace NzbDrone.Core.DataAugmentation.Scene
public class SceneMappingProxy : ISceneMappingProxy public class SceneMappingProxy : ISceneMappingProxy
{ {
private readonly IHttpProvider _httpProvider; private readonly IHttpProvider _httpProvider;
private readonly IConfigService _configService;
public SceneMappingProxy(IHttpProvider httpProvider, IConfigService configService) public SceneMappingProxy(IHttpProvider httpProvider)
{ {
_httpProvider = httpProvider; _httpProvider = httpProvider;
_configService = configService;
} }
public List<SceneMapping> Fetch() 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); 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 var history = new History
{ {
EventType = HistoryEventType.Grabbed, EventType = HistoryEventType.Grabbed,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.Episode.ParsedEpisodeInfo.Quality, Quality = message.Episode.ParsedEpisodeInfo.Quality,
SourceTitle = message.Episode.Report.Title, SourceTitle = message.Episode.Report.Title,
@ -81,20 +81,22 @@ namespace NzbDrone.Core.History
public void Handle(EpisodeImportedEvent message) public void Handle(EpisodeImportedEvent message)
{ {
foreach (var episode in message.EpisodeFile.Episodes.Value) foreach (var episode in message.DroppedEpisode.Episodes)
{ {
var history = new History var history = new History
{ {
EventType = HistoryEventType.DownloadFolderImported, EventType = HistoryEventType.DownloadFolderImported,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.EpisodeFile.Quality, Quality = message.DroppedEpisode.Quality,
SourceTitle = message.EpisodeFile.Path, SourceTitle = message.ImportedEpisode.SceneName,
SeriesId = message.EpisodeFile.SeriesId, SeriesId = message.ImportedEpisode.SeriesId,
EpisodeId = episode.Id EpisodeId = episode.Id
}; };
history.Data.Add("Path", message.EpisodeFile.Path); //Won't have a value since we publish this event before saving to DB.
history.Data.Add("Filename", Path.GetFileNameWithoutExtension(message.EpisodeFile.Path)); //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); _historyRepository.Insert(history);
} }

@ -4,6 +4,7 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using NLog; using NLog;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -12,7 +13,7 @@ namespace NzbDrone.Core.Indexers
{ {
public interface IParseFeed public interface IParseFeed
{ {
IEnumerable<ReportInfo> Process(Stream source, string url); IEnumerable<ReportInfo> Process(string xml, string url);
} }
public class BasicRssParser : IParseFeed public class BasicRssParser : IParseFeed
@ -24,34 +25,37 @@ namespace NzbDrone.Core.Indexers
_logger = LogManager.GetCurrentClassLogger(); _logger = LogManager.GetCurrentClassLogger();
} }
public IEnumerable<ReportInfo> Process(Stream source, string url) public IEnumerable<ReportInfo> Process(string xml, string url)
{ {
var document = XDocument.Load(source); using (var xmlTextReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { ProhibitDtd = false, IgnoreComments = true }))
var items = document.Descendants("item"); {
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) foreach (var item in items)
{
try
{ {
var reportInfo = ParseFeedItem(item); try
if (reportInfo != null)
{ {
reportInfo.NzbUrl = GetNzbUrl(item); var reportInfo = ParseFeedItem(item);
reportInfo.NzbInfoUrl = GetNzbInfoUrl(item); if (reportInfo != null)
{
result.Add(reportInfo); 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 try
{ {
_logger.Trace("Downloading Feed " + url); _logger.Trace("Downloading Feed " + url);
var stream = _httpProvider.DownloadStream(url); var xml = _httpProvider.DownloadString(url);
result.AddRange(indexer.Parser.Process(stream, url)); if (!string.IsNullOrWhiteSpace(xml))
{
result.AddRange(indexer.Parser.Process(xml, url));
}
else
{
_logger.Warn("{0} returned empty response.", url);
}
} }
catch (WebException webException) 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); _logger.Warn("{0} server is currently unavailable. {1} {2}", indexer.Name, url, webException.Message);
} }
else else
{ {
webException.Data.Add("FeedUrl", url); _logger.Warn("{0} {1} {2}", indexer.Name, url, webException.Message);
_logger.WarnException("An error occurred while processing feed. " + url, webException);
} }
} }
catch (Exception feedEx) catch (Exception feedEx)

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

@ -44,30 +44,31 @@ namespace NzbDrone.Core.MediaCover
foreach (var cover in series.Images) foreach (var cover in series.Images)
{ {
var fileName = GetCoverPath(series.Id, cover.CoverType); 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) 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) public void HandleAsync(SeriesDeletedEvent message)

@ -24,9 +24,9 @@ namespace NzbDrone.Core.MediaFiles
private const string EXTENSIONS = private const string EXTENSIONS =
//XBMC //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 " + ".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 //Other
".m2ts"; ".m2ts";

@ -13,8 +13,8 @@ namespace NzbDrone.Core.MediaFiles
{ {
public interface IMoveEpisodeFiles public interface IMoveEpisodeFiles
{ {
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series); string MoveEpisodeFile(EpisodeFile episodeFile, Series series);
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode); string MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
} }
public class MoveEpisodeFiles : IMoveEpisodeFiles public class MoveEpisodeFiles : IMoveEpisodeFiles
@ -38,27 +38,26 @@ namespace NzbDrone.Core.MediaFiles
_logger = logger; _logger = logger;
} }
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series) public string MoveEpisodeFile(EpisodeFile episodeFile, Series series)
{ {
var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id); var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id);
var newFileName = _buildFileNames.BuildFilename(episodes, series, episodeFile); 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 newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile);
var destinationFilename = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path)); var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
episodeFile = MoveFile(episodeFile, destinationFilename); MoveFile(episodeFile, filePath);
_messageAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode)); return filePath;
return episodeFile;
} }
private EpisodeFile MoveFile(EpisodeFile episodeFile, string destinationFilename) private void MoveFile(EpisodeFile episodeFile, string destinationFilename)
{ {
if (!_diskProvider.FileExists(episodeFile.Path)) if (!_diskProvider.FileExists(episodeFile.Path))
{ {
@ -85,10 +84,6 @@ namespace NzbDrone.Core.MediaFiles
_logger.Debug("Unable to apply folder permissions to: ", destinationFilename); _logger.Debug("Unable to apply folder permissions to: ", destinationFilename);
_logger.TraceException(ex.Message, ex); _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); List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownloads = false);
} }
public class ImportApprovedEpisodes : IImportApprovedEpisodes public class ImportApprovedEpisodes : IImportApprovedEpisodes
{ {
private readonly IUpgradeMediaFiles _episodeFileUpgrader; private readonly IUpgradeMediaFiles _episodeFileUpgrader;
@ -62,15 +62,17 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path); episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path);
episodeFile.Quality = localEpisode.Quality; episodeFile.Quality = localEpisode.Quality;
episodeFile.SeasonNumber = localEpisode.SeasonNumber; episodeFile.SeasonNumber = localEpisode.SeasonNumber;
episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
episodeFile.Episodes = localEpisode.Episodes; episodeFile.Episodes = localEpisode.Episodes;
if (newDownload) if (newDownload)
{ {
episodeFile = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode); episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
_messageAggregator.PublishEvent(new EpisodeImportedEvent(episodeFile)); episodeFile.Path = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode);
_messageAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile));
_messageAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode));
} }
_mediaFileService.Add(episodeFile); _mediaFileService.Add(episodeFile);
imported.Add(importDecision); imported.Add(importDecision);
} }

@ -24,7 +24,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
try try
{ {
var path = Directory.GetParent(localEpisode.Series.Path); 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()) if (freeSpace < localEpisode.Size + 100.Megabytes())
{ {

@ -1,14 +1,17 @@
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.Events namespace NzbDrone.Core.MediaFiles.Events
{ {
public class EpisodeImportedEvent : IEvent 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>(); var renamed = new List<EpisodeFile>();
foreach (var file in episodeFiles) foreach (var episodeFile in episodeFiles)
{ {
try try
{ {
var episodeFile = file;
_logger.Trace("Renaming episode file: {0}", episodeFile); _logger.Trace("Renaming episode file: {0}", episodeFile);
episodeFile = _episodeFileMover.MoveEpisodeFile(episodeFile, series); episodeFile.Path = _episodeFileMover.MoveEpisodeFile(episodeFile, series);
_mediaFileService.Update(episodeFile); _mediaFileService.Update(episodeFile);
renamed.Add(episodeFile); renamed.Add(episodeFile);
@ -54,7 +52,7 @@ namespace NzbDrone.Core.MediaFiles
} }
catch (Exception ex) 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 public interface IUpgradeMediaFiles
{ {
EpisodeFile UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode); string UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
} }
public class UpgradeMediaFileService : IUpgradeMediaFiles public class UpgradeMediaFileService : IUpgradeMediaFiles
@ -31,7 +31,7 @@ namespace NzbDrone.Core.MediaFiles
_logger = logger; _logger = logger;
} }
public EpisodeFile UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode) public string UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
{ {
var existingFiles = localEpisode.Episodes var existingFiles = localEpisode.Episodes
.Where(e => e.EpisodeFileId > 0) .Where(e => e.EpisodeFileId > 0)

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

@ -17,7 +17,7 @@ namespace NzbDrone.Core.Parser.Model
public bool IsRecentEpisode() 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() 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 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) public static ParsedEpisodeInfo ParsePath(string path)
{ {
@ -119,7 +120,7 @@ namespace NzbDrone.Core.Parser
break; break;
result.Language = ParseLanguage(title); result.Language = ParseLanguage(title);
result.Quality = ParseQuality(title); result.Quality = QualityParser.ParseQuality(title);
return result; return result;
} }
} }
@ -240,155 +241,6 @@ namespace NzbDrone.Core.Parser
return parseResult.SeriesTitle; 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) private static Language ParseLanguage(string title)
{ {
var lowerTitle = title.ToLower(); var lowerTitle = title.ToLower();
@ -461,6 +313,9 @@ namespace NzbDrone.Core.Parser
if (match.Groups["greek"].Captures.Cast<Capture>().Any()) if (match.Groups["greek"].Captures.Cast<Capture>().Any())
return Language.Greek; return Language.Greek;
if (match.Groups["french"].Success)
return Language.French;
return Language.English; 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.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events; using NzbDrone.Core.Tv.Events;
@ -12,31 +14,37 @@ namespace NzbDrone.Core.Providers
{ {
void UpdateMappings(); void UpdateMappings();
void UpdateMappings(int seriesId); void UpdateMappings(int seriesId);
void UpdateMappings(Series series);
void PerformUpdate(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 IEpisodeService _episodeService;
private readonly IXemCommunicationProvider _xemCommunicationProvider; private readonly IXemCommunicationProvider _xemCommunicationProvider;
private readonly ISeriesService _seriesService; private readonly ISeriesService _seriesService;
private readonly ICached<bool> _cache;
private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); 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"); if (seriesService == null) throw new ArgumentNullException("seriesService");
_episodeService = episodeService; _episodeService = episodeService;
_xemCommunicationProvider = xemCommunicationProvider; _xemCommunicationProvider = xemCommunicationProvider;
_seriesService = seriesService; _seriesService = seriesService;
_cache = cacheManger.GetCache<bool>(GetType());
} }
public void UpdateMappings() public void UpdateMappings()
{ {
_logger.Trace("Starting scene numbering update"); _logger.Trace("Starting scene numbering update");
try try
{ {
var ids = _xemCommunicationProvider.GetXemSeriesIds(); var ids = GetXemSeriesIds();
var series = _seriesService.GetAllSeries(); var series = _seriesService.GetAllSeries();
var wantedSeries = series.Where(s => ids.Contains(s.TvdbId)).ToList(); 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); _logger.Trace("Series could not be found: {0}", seriesId);
return; return;
} }
UpdateMappings(series);
}
var xemIds = _xemCommunicationProvider.GetXemSeriesIds(); public void UpdateMappings(Series series)
{
if (!xemIds.Contains(series.TvdbId)) 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; 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) public void Execute(UpdateXemMappingsCommand message)
{ {
if (message.SeriesId.HasValue) if (message.SeriesId.HasValue)
@ -139,7 +164,12 @@ namespace NzbDrone.Core.Providers
public void Handle(SeriesUpdatedEvent message) 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 string Path { get; set; }
public long FreeSpace { get; set; } public long? FreeSpace { get; set; }
public List<UnmappedFolder> UnmappedFolders { get; set; } public List<UnmappedFolder> UnmappedFolders { get; set; }
} }

@ -17,7 +17,7 @@ namespace NzbDrone.Core.RootFolders
RootFolder Add(RootFolder rootDir); RootFolder Add(RootFolder rootDir);
void Remove(int id); void Remove(int id);
List<UnmappedFolder> GetUnmappedFolders(string path); List<UnmappedFolder> GetUnmappedFolders(string path);
Dictionary<string, long> FreeSpaceOnDrives(); Dictionary<string, long?> FreeSpaceOnDrives();
RootFolder Get(int id); RootFolder Get(int id);
} }
@ -55,7 +55,7 @@ namespace NzbDrone.Core.RootFolders
{ {
if (_diskProvider.FolderExists(folder.Path)) if (_diskProvider.FolderExists(folder.Path))
{ {
folder.FreeSpace = _diskProvider.GetAvilableSpace(folder.Path); folder.FreeSpace = _diskProvider.GetAvailableSpace(folder.Path);
folder.UnmappedFolders = GetUnmappedFolders(folder.Path); folder.UnmappedFolders = GetUnmappedFolders(folder.Path);
} }
}); });
@ -82,7 +82,7 @@ namespace NzbDrone.Core.RootFolders
_rootFolderRepository.Insert(rootFolder); _rootFolderRepository.Insert(rootFolder);
rootFolder.FreeSpace = _diskProvider.GetAvilableSpace(rootFolder.Path); rootFolder.FreeSpace = _diskProvider.GetAvailableSpace(rootFolder.Path);
rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path); rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path);
return rootFolder; return rootFolder;
} }
@ -126,9 +126,9 @@ namespace NzbDrone.Core.RootFolders
return results; 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(); var rootDirs = All();
@ -140,7 +140,7 @@ namespace NzbDrone.Core.RootFolders
{ {
try try
{ {
freeSpace.Add(pathRoot, _diskProvider.GetAvilableSpace(rootDir.Path)); freeSpace.Add(pathRoot, _diskProvider.GetAvailableSpace(rootDir.Path));
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -155,7 +155,7 @@ namespace NzbDrone.Core.RootFolders
public RootFolder Get(int id) public RootFolder Get(int id)
{ {
var rootFolder = _rootFolderRepository.Get(id); var rootFolder = _rootFolderRepository.Get(id);
rootFolder.FreeSpace = _diskProvider.GetAvilableSpace(rootFolder.Path); rootFolder.FreeSpace = _diskProvider.GetAvailableSpace(rootFolder.Path);
rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path); rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path);
return rootFolder; 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 successCount = 0;
var failCount = 0; var failCount = 0;
var existinEpisodes = _episodeService.GetEpisodeBySeries(series.Id); var existingEpisodes = _episodeService.GetEpisodeBySeries(series.Id);
var seasons = _seasonService.GetSeasonsBySeries(series.Id); var seasons = _seasonService.GetSeasonsBySeries(series.Id);
var updateList = new List<Episode>(); var updateList = new List<Episode>();
@ -44,11 +44,11 @@ namespace NzbDrone.Core.Tv
{ {
try 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) if (episodeToUpdate != null)
{ {
existinEpisodes.Remove(episodeToUpdate); existingEpisodes.Remove(episodeToUpdate);
updateList.Add(episodeToUpdate); updateList.Add(episodeToUpdate);
} }
else else
@ -82,11 +82,10 @@ namespace NzbDrone.Core.Tv
AdjustMultiEpisodeAirTime(series, allEpisodes); AdjustMultiEpisodeAirTime(series, allEpisodes);
_episodeService.DeleteMany(existinEpisodes); _episodeService.DeleteMany(existingEpisodes);
_episodeService.UpdateMany(updateList); _episodeService.UpdateMany(updateList);
_episodeService.InsertMany(newList); _episodeService.InsertMany(newList);
if (newList.Any()) if (newList.Any())
{ {
_messageAggregator.PublishEvent(new EpisodeInfoAddedEvent(newList, series)); _messageAggregator.PublishEvent(new EpisodeInfoAddedEvent(newList, series));
@ -97,6 +96,11 @@ namespace NzbDrone.Core.Tv
_messageAggregator.PublishEvent(new EpisodeInfoUpdatedEvent(updateList)); _messageAggregator.PublishEvent(new EpisodeInfoUpdatedEvent(updateList));
} }
if (existingEpisodes.Any())
{
_messageAggregator.PublishEvent(new EpisodeInfoDeletedEvent(updateList));
}
if (failCount != 0) if (failCount != 0)
{ {
_logger.Info("Finished episode refresh for series: {0}. Successful: {1} - Failed: {2} ", _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(); var latestAvailable = _updatePackageProvider.GetLatestUpdate();
if (latestAvailable == null || latestAvailable.Version <= BuildInfo.Version) if (latestAvailable == null)
{ {
_logger.Debug("No update available."); _logger.Debug("No update available.");
return null; return null;

@ -1,11 +1,18 @@
using System; using System;
using Newtonsoft.Json;
namespace NzbDrone.Core.Update namespace NzbDrone.Core.Update
{ {
public class UpdatePackage public class UpdatePackage
{ {
public string Url { get; set; } public String Id { get; set; }
public string FileName { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
public Version Version { get; set; } 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.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Newtonsoft.Json;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -11,56 +12,30 @@ namespace NzbDrone.Core.Update
{ {
public interface IUpdatePackageProvider public interface IUpdatePackageProvider
{ {
IEnumerable<UpdatePackage> GetAvailablePackages();
UpdatePackage GetLatestUpdate(); UpdatePackage GetLatestUpdate();
} }
public class UpdatePackageProvider : IUpdatePackageProvider public class UpdatePackageProvider : IUpdatePackageProvider
{ {
private readonly IConfigFileProvider _configService; private readonly IConfigFileProvider _configFileProvider;
private readonly IHttpProvider _httpProvider; private readonly IHttpProvider _httpProvider;
private readonly Logger _logger; private readonly Logger _logger;
private static readonly Regex ParseRegex = new Regex(@"(?:\>)(?<filename>NzbDrone.+?(?<version>(?<=\.)\d+\.\d+\.\d+\.\d+).+?)(?:\<\/a\>)", public UpdatePackageProvider(IConfigFileProvider configFileProvider, IHttpProvider httpProvider, Logger logger)
RegexOptions.IgnoreCase);
public UpdatePackageProvider(IConfigFileProvider configService, IHttpProvider httpProvider, Logger logger)
{ {
_configService = configService; _configFileProvider = configFileProvider;
_httpProvider = httpProvider; _httpProvider = httpProvider;
_logger = logger; _logger = logger;
} }
public IEnumerable<UpdatePackage> GetAvailablePackages() public UpdatePackage GetLatestUpdate()
{ {
var updateList = new List<UpdatePackage>(); var url = String.Format("{0}/v1/update/{1}?version={2}", Services.RootUrl, _configFileProvider.Branch, BuildInfo.Version);
var update = JsonConvert.DeserializeObject<UpdatePackageAvailable>(_httpProvider.DownloadString(url));
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);
foreach (Match match in matches) if (!update.Available) return null;
{
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);
}
_logger.Debug("Found {0} update packages", updateUrl.Length); return update.UpdatePackage;
return updateList;
}
public UpdatePackage GetLatestUpdate()
{
return GetAvailablePackages().OrderByDescending(c => c.Version).FirstOrDefault();
} }
} }
} }

@ -23,6 +23,7 @@ namespace NzbDrone.Core.Validation
public static IRuleBuilderOptions<T, string> ValidRootUrl<T>(this IRuleBuilder<T, string> ruleBuilder) 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"); 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 System;
using NetFwTypeLib; using NetFwTypeLib;
using NLog; using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
namespace NzbDrone.Host.AccessControl namespace NzbDrone.Host.AccessControl
@ -31,9 +32,6 @@ namespace NzbDrone.Host.AccessControl
return; return;
} }
CloseFirewallPort();
//Open the new port
OpenFirewallPort(_configFileProvider.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() private bool IsFirewallEnabled()
{ {
if (OsInfo.IsLinux) return false;
try try
{ {
var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false);
@ -135,7 +105,5 @@ namespace NzbDrone.Host.AccessControl
return false; return false;
} }
} }
} }
} }

@ -9,6 +9,7 @@ namespace NzbDrone.Host.AccessControl
public interface IUrlAclAdapter public interface IUrlAclAdapter
{ {
void RefreshRegistration(); void RefreshRegistration();
string UrlAcl { get; }
} }
public class UrlAclAdapter : IUrlAclAdapter public class UrlAclAdapter : IUrlAclAdapter
@ -24,18 +25,25 @@ namespace NzbDrone.Host.AccessControl
_logger = logger; _logger = logger;
} }
public string UrlAcl
{
get
{
return "http://*:" + _configFileProvider.Port + "/";
}
}
public void RefreshRegistration() public void RefreshRegistration()
{ {
if (OsInfo.Version.Major < 6) if (OsInfo.Version.Major < 6)
return; return;
RegisterUrl(_configFileProvider.Port); RegisterUrl();
} }
private void RegisterUrl()
private void RegisterUrl(int portNumber)
{ {
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); RunNetsh(arguments);
} }

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

@ -122,6 +122,9 @@
<Compile Include="AccessControl\FirewallAdapter.cs" /> <Compile Include="AccessControl\FirewallAdapter.cs" />
<Compile Include="AccessControl\UrlAclAdapter.cs" /> <Compile Include="AccessControl\UrlAclAdapter.cs" />
<Compile Include="IUserAlert.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="PlatformValidation.cs" />
<Compile Include="MainAppContainerBuilder.cs" /> <Compile Include="MainAppContainerBuilder.cs" />
<Compile Include="ApplicationModes.cs" /> <Compile Include="ApplicationModes.cs" />

@ -4,7 +4,6 @@
{ {
string AppUrl { get; } string AppUrl { get; }
void StartServer(); void StartServer();
void RestartServer();
void StopServer(); 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 System.Linq;
using Microsoft.Owin.Hosting; using Microsoft.Owin.Hosting;
using NLog; using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Security; using NzbDrone.Common.Security;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Host.AccessControl;
using NzbDrone.Host.Owin.MiddleWare; using NzbDrone.Host.Owin.MiddleWare;
using Owin; using Owin;
@ -14,13 +16,20 @@ namespace NzbDrone.Host.Owin
{ {
private readonly IConfigFileProvider _configFileProvider; private readonly IConfigFileProvider _configFileProvider;
private readonly IEnumerable<IOwinMiddleWare> _owinMiddleWares; private readonly IEnumerable<IOwinMiddleWare> _owinMiddleWares;
private readonly IRuntimeInfo _runtimeInfo;
private readonly IUrlAclAdapter _urlAclAdapter;
private readonly IFirewallAdapter _firewallAdapter;
private readonly Logger _logger; private readonly Logger _logger;
private IDisposable _host; 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; _configFileProvider = configFileProvider;
_owinMiddleWares = owinMiddleWares; _owinMiddleWares = owinMiddleWares;
_runtimeInfo = runtimeInfo;
_urlAclAdapter = urlAclAdapter;
_firewallAdapter = firewallAdapter;
_logger = logger; _logger = logger;
} }
@ -28,16 +37,20 @@ namespace NzbDrone.Host.Owin
{ {
IgnoreCertErrorPolicy.Register(); 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" 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) private void BuildApp(IAppBuilder appBuilder)
@ -56,14 +69,6 @@ namespace NzbDrone.Host.Owin
get { return string.Format("http://localhost:{0}", _configFileProvider.Port); } get { return string.Format("http://localhost:{0}", _configFileProvider.Port); }
} }
public void RestartServer()
{
_logger.Warn("Attempting to restart server.");
StopServer();
StartServer();
}
public void StopServer() public void StopServer()
{ {
if (_host == null) return; 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 NUnit.Framework;
using NzbDrone.Api.Series; using NzbDrone.Api.Series;
using System.Linq; using System.Linq;
using NzbDrone.Test.Common;
namespace NzbDrone.Integration.Test namespace NzbDrone.Integration.Test
{ {
@ -15,7 +16,7 @@ namespace NzbDrone.Integration.Test
var series = Series.Lookup("archer").First(); var series = Series.Lookup("archer").First();
series.QualityProfileId = 1; series.QualityProfileId = 1;
series.Path = @"C:\Test\Archer"; series.Path = @"C:\Test\Archer".AsOsAgnostic();
series = Series.Post(series); series = Series.Post(series);
@ -57,4 +58,4 @@ namespace NzbDrone.Integration.Test
Episodes.Put(updatedEpisode).Monitored.Should().BeFalse(); Episodes.Put(updatedEpisode).Monitored.Should().BeFalse();
} }
} }
} }

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

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

@ -53,11 +53,17 @@ namespace NzbDrone.Integration.Test
Assert.Fail("Process has exited"); 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; return;
} }
Console.WriteLine("Waiting for NzbDrone to start. Response Status : {0} [{1}] {2}", statusCall.ResponseStatus, statusCall.StatusDescription, statusCall.ErrorException);
Thread.Sleep(500); Thread.Sleep(500);
} }
} }

@ -58,5 +58,17 @@ namespace NzbDrone.Integration.Test
RootFolders.All().Should().BeEmpty(); 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 NUnit.Framework;
using NzbDrone.Api.Series; using NzbDrone.Api.Series;
using System.Linq; using System.Linq;
using NzbDrone.Test.Common;
namespace NzbDrone.Integration.Test namespace NzbDrone.Integration.Test
{ {
@ -15,7 +16,7 @@ namespace NzbDrone.Integration.Test
var series = Series.Lookup("archer").First(); var series = Series.Lookup("archer").First();
series.QualityProfileId = 1; series.QualityProfileId = 1;
series.Path = @"C:\Test\Archer"; series.Path = @"C:\Test\Archer".AsOsAgnostic();
series = Series.Post(series); series = Series.Post(series);
@ -57,4 +58,4 @@ namespace NzbDrone.Integration.Test
Seasons.Put(updatedSeason).Monitored.Should().BeFalse(); Seasons.Put(updatedSeason).Monitored.Should().BeFalse();
} }
} }
} }

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

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <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="FluentValidation" version="4.0.0.1" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.Client" version="1.1.3" targetFramework="net40" /> <package id="Microsoft.AspNet.SignalR.Client" version="1.1.3" targetFramework="net40" />
<package id="Microsoft.Owin" version="1.1.0-beta2" targetFramework="net40" /> <package id="Microsoft.Owin" version="1.1.0-beta2" targetFramework="net40" />

@ -34,8 +34,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FluentAssertions"> <Reference Include="FluentAssertions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Practices.ServiceLocation"> <Reference Include="Microsoft.Practices.ServiceLocation">
<HintPath>..\packages\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll</HintPath> <HintPath>..\packages\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll</HintPath>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="CommonServiceLocator" version="1.0" /> <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="Moq" version="4.0.10827" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" /> <package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" /> <package id="NLog" 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"> <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> <HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference> </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> <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>
<Reference Include="Moq"> <Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <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="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1.1" /> <package id="NBuilder" version="3.0.1.1" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" /> <package id="NLog" version="2.0.1.2" targetFramework="net40" />

@ -83,6 +83,10 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference> </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"> <Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath> <HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>

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

@ -5,5 +5,6 @@
<package id="Microsoft.Owin" version="1.1.0-beta2" targetFramework="net40" /> <package id="Microsoft.Owin" version="1.1.0-beta2" targetFramework="net40" />
<package id="Microsoft.Owin.Hosting" version="1.1.0-beta2" targetFramework="net40" /> <package id="Microsoft.Owin.Hosting" version="1.1.0-beta2" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" 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" /> <package id="Owin" version="1.0" targetFramework="net40" />
</packages> </packages>

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

@ -7,10 +7,11 @@ define(
'AddSeries/RootFolders/Collection', 'AddSeries/RootFolders/Collection',
'AddSeries/RootFolders/Model', 'AddSeries/RootFolders/Model',
'Shared/LoadingView', 'Shared/LoadingView',
'Mixins/AsValidatedView',
'Mixins/AutoComplete' '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', template: 'AddSeries/RootFolders/LayoutTemplate',
ui: { ui: {
@ -55,12 +56,16 @@ define(
Path: this.ui.pathInput.val() Path: this.ui.pathInput.val()
}); });
RootFolderCollection.add(newDir); this.bindToModelValidation(newDir);
newDir.save().done(function () { newDir.save().done(function () {
RootFolderCollection.add(newDir);
self.trigger('folderSelected', {model: newDir}); self.trigger('folderSelected', {model: newDir});
}); });
} }
}); });
return AsValidatedView.apply(layout);
}); });

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

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

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

Loading…
Cancel
Save