diff --git a/Exceptron.Client/ExceptronClient.cs b/Exceptron.Client/ExceptronClient.cs index b49af3bc4..149a739d7 100644 --- a/Exceptron.Client/ExceptronClient.cs +++ b/Exceptron.Client/ExceptronClient.cs @@ -149,7 +149,11 @@ namespace Exceptron.Client SetHttpInfo(exceptionData, report); SetEnviromentInfo(report); - return RestClient.Put(Configuration.Host, report); + var exceptionResponse = RestClient.Put(Configuration.Host, report); + + exceptionData.Exception.Data["et"] = exceptionResponse.RefId; + + return exceptionResponse; } catch (Exception e) { diff --git a/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj b/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj index 7fa8111c1..ddf2f38e7 100644 --- a/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj +++ b/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj @@ -36,9 +36,9 @@ ..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll - + False - ..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll + ..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll diff --git a/NzbDrone.Api.Test/packages.config b/NzbDrone.Api.Test/packages.config index 139196ed3..ce6653d25 100644 --- a/NzbDrone.Api.Test/packages.config +++ b/NzbDrone.Api.Test/packages.config @@ -1,8 +1,8 @@  - + + - \ No newline at end of file diff --git a/NzbDrone.Api/Frontend/Mappers/StaticResourceMapper.cs b/NzbDrone.Api/Frontend/Mappers/StaticResourceMapper.cs index b97f63943..da9fac532 100644 --- a/NzbDrone.Api/Frontend/Mappers/StaticResourceMapper.cs +++ b/NzbDrone.Api/Frontend/Mappers/StaticResourceMapper.cs @@ -25,7 +25,7 @@ namespace NzbDrone.Api.Frontend.Mappers public override bool CanHandle(string resourceUrl) { - return resourceUrl.StartsWith("/Content") || resourceUrl.EndsWith(".js") || resourceUrl.EndsWith(".css"); + return resourceUrl.StartsWith("/Content") || resourceUrl.EndsWith(".js") || resourceUrl.EndsWith(".css") || resourceUrl.EndsWith(".ico"); } } } \ No newline at end of file diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 5210ef0a6..d0a03bd17 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -164,6 +164,7 @@ + diff --git a/NzbDrone.Api/RootFolders/RootFolderModule.cs b/NzbDrone.Api/RootFolders/RootFolderModule.cs index 5c13a736c..7bcc4aa52 100644 --- a/NzbDrone.Api/RootFolders/RootFolderModule.cs +++ b/NzbDrone.Api/RootFolders/RootFolderModule.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using NzbDrone.Core.RootFolders; using NzbDrone.Api.Mapping; +using NzbDrone.Api.Validation; namespace NzbDrone.Api.RootFolders { @@ -17,6 +18,8 @@ namespace NzbDrone.Api.RootFolders GetResourceById = GetRootFolder; CreateResource = CreateRootFolder; DeleteResource = DeleteFolder; + + SharedValidator.RuleFor(c=>c.Path).IsValidPath(); } private RootFolderResource GetRootFolder(int id) diff --git a/NzbDrone.Api/RootFolders/RootFolderResource.cs b/NzbDrone.Api/RootFolders/RootFolderResource.cs index 05af828c1..43fcbe667 100644 --- a/NzbDrone.Api/RootFolders/RootFolderResource.cs +++ b/NzbDrone.Api/RootFolders/RootFolderResource.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Api.RootFolders public class RootFolderResource : RestResource { public String Path { get; set; } - public Int64 FreeSpace { get; set; } + public Int64? FreeSpace { get; set; } public List UnmappedFolders { get; set; } } diff --git a/NzbDrone.Api/Series/SeriesModule.cs b/NzbDrone.Api/Series/SeriesModule.cs index 57caeb7a6..fe7b820a6 100644 --- a/NzbDrone.Api/Series/SeriesModule.cs +++ b/NzbDrone.Api/Series/SeriesModule.cs @@ -31,10 +31,10 @@ namespace NzbDrone.Api.Series SharedValidator.RuleFor(s => s.QualityProfileId).ValidId(); - PutValidator.RuleFor(s => s.Path).NotEmpty(); + PutValidator.RuleFor(s => s.Path).IsValidPath(); - PostValidator.RuleFor(s => s.Path).NotEmpty().When(s => String.IsNullOrEmpty(s.RootFolderPath)); - PostValidator.RuleFor(s => s.RootFolderPath).NotEmpty().When(s => String.IsNullOrEmpty(s.Path)); + PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => String.IsNullOrEmpty(s.RootFolderPath)); + PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => String.IsNullOrEmpty(s.Path)); PostValidator.RuleFor(s => s.Title).NotEmpty(); } diff --git a/NzbDrone.Api/Update/UpdateModule.cs b/NzbDrone.Api/Update/UpdateModule.cs index 6fdb1ca50..8a95efa4b 100644 --- a/NzbDrone.Api/Update/UpdateModule.cs +++ b/NzbDrone.Api/Update/UpdateModule.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json; using NzbDrone.Api.REST; using NzbDrone.Core.Update; using NzbDrone.Api.Mapping; @@ -32,7 +33,13 @@ namespace NzbDrone.Api.Update public class UpdateResource : RestResource { + public String Id { get; set; } + + [JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))] public Version Version { get; set; } + + public String Branch { get; set; } + public DateTime ReleaseDate { get; set; } public String FileName { get; set; } public String Url { get; set; } } diff --git a/NzbDrone.Api/Validation/PathValidator.cs b/NzbDrone.Api/Validation/PathValidator.cs new file mode 100644 index 000000000..f7cf37eab --- /dev/null +++ b/NzbDrone.Api/Validation/PathValidator.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Validation/RuleBuilderExtensions.cs b/NzbDrone.Api/Validation/RuleBuilderExtensions.cs index 23dc34c77..b142f5a56 100644 --- a/NzbDrone.Api/Validation/RuleBuilderExtensions.cs +++ b/NzbDrone.Api/Validation/RuleBuilderExtensions.cs @@ -1,4 +1,6 @@ -using System.Text.RegularExpressions; +using System; +using System.Linq.Expressions; +using System.Text.RegularExpressions; using FluentValidation; using FluentValidation.Validators; @@ -20,5 +22,10 @@ namespace NzbDrone.Api.Validation { return ruleBuilder.SetValidator(new RegularExpressionValidator("^http(s)?://", RegexOptions.IgnoreCase)).WithMessage("must start with http:// or https://"); } + + public static IRuleBuilderOptions IsValidPath(this IRuleBuilder ruleBuilder) + { + return ruleBuilder.SetValidator(new PathValidator()); + } } } \ No newline at end of file diff --git a/NzbDrone.App.Test/NzbDrone.Host.Test.csproj b/NzbDrone.App.Test/NzbDrone.Host.Test.csproj index a2dbe6680..3581eb9cb 100644 --- a/NzbDrone.App.Test/NzbDrone.Host.Test.csproj +++ b/NzbDrone.App.Test/NzbDrone.Host.Test.csproj @@ -37,9 +37,9 @@ ..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll - + False - ..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll + ..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll diff --git a/NzbDrone.App.Test/packages.config b/NzbDrone.App.Test/packages.config index 3e6b4e094..6f925b437 100644 --- a/NzbDrone.App.Test/packages.config +++ b/NzbDrone.App.Test/packages.config @@ -1,6 +1,6 @@  - + diff --git a/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs b/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs new file mode 100644 index 000000000..8359ad481 --- /dev/null +++ b/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs @@ -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 + { + 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(); + } + } +} diff --git a/NzbDrone.Common.Test/DiskProviderTests/IsParentFixture.cs b/NzbDrone.Common.Test/DiskProviderTests/IsParentFixture.cs index 8359ad481..b29f60180 100644 --- a/NzbDrone.Common.Test/DiskProviderTests/IsParentFixture.cs +++ b/NzbDrone.Common.Test/DiskProviderTests/IsParentFixture.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.IO; using FluentAssertions; using NUnit.Framework; using NzbDrone.Test.Common; @@ -9,32 +6,38 @@ using NzbDrone.Test.Common; namespace NzbDrone.Common.Test.DiskProviderTests { [TestFixture] - public class IsParentFixture : TestBase + public class FreeSpaceFixture : TestBase { - private string _parent = @"C:\Test".AsOsAgnostic(); - [Test] - public void should_return_false_when_not_a_child() + public void should_get_free_space_for_folder() { - var path = @"C:\Another Folder".AsOsAgnostic(); + var path = @"C:\".AsOsAgnostic(); - Subject.IsParent(_parent, path).Should().BeFalse(); + Subject.GetAvailableSpace(path).Should().NotBe(0); } [Test] - public void should_return_true_when_folder_is_parent_of_another_folder() + public void should_get_free_space_for_folder_that_doesnt_exist() { - var path = @"C:\Test\TV".AsOsAgnostic(); + var path = @"C:\".AsOsAgnostic(); - Subject.IsParent(_parent, path).Should().BeTrue(); + Subject.GetAvailableSpace(Path.Combine(path, "invalidFolder")).Should().NotBe(0); + } + + + [Test] + public void should_get_free_space_for_drive_that_doesnt_exist() + { + WindowsOnly(); + + Assert.Throws(() => Subject.GetAvailableSpace("J:\\").Should().NotBe(0)); } [Test] - public void should_return_true_when_folder_is_parent_of_a_file() + public void should_be_able_to_check_space_on_ramdrive() { - var path = @"C:\Test\30.Rock.S01E01.Pilot.avi".AsOsAgnostic(); - - Subject.IsParent(_parent, path).Should().BeTrue(); + LinuxOnly(); + Subject.GetAvailableSpace("/run/").Should().NotBe(0); } } } diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj index 1dd3ccd78..4c22a7aeb 100644 --- a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj @@ -34,9 +34,9 @@ MinimumRecommendedRules.ruleset - + False - ..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll + ..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll @@ -62,6 +62,7 @@ + diff --git a/NzbDrone.Common.Test/PathExtensionFixture.cs b/NzbDrone.Common.Test/PathExtensionFixture.cs index 739344727..907d13d4c 100644 --- a/NzbDrone.Common.Test/PathExtensionFixture.cs +++ b/NzbDrone.Common.Test/PathExtensionFixture.cs @@ -102,10 +102,18 @@ namespace NzbDrone.Common.Test } [Test] - public void get_actual_casing_for_none_existing_file_should_throw() + public void get_actual_casing_for_none_existing_file_return_partially_fixed_result() { WindowsOnly(); - Assert.Throws(() => "C:\\InValidFolder\\invalidfile.exe".GetActualCasing()); + "C:\\WINDOWS\\invalidfile.exe".GetActualCasing().Should().Be("C:\\Windows\\invalidfile.exe"); + } + + + [Test] + public void get_actual_casing_for_none_existing_folder_return_partially_fixed_result() + { + WindowsOnly(); + "C:\\WINDOWS\\SYSTEM32\\FAKEFOLDER\\invalidfile.exe".GetActualCasing().Should().Be("C:\\Windows\\System32\\FAKEFOLDER\\invalidfile.exe"); } [Test] @@ -117,14 +125,7 @@ namespace NzbDrone.Common.Test path.ToLower().GetActualCasing().Should().Be(path); } - [Test] - public void get_actual_casing_should_return_origibal_value_in_linux() - { - LinuxOnly(); - var path = Process.GetCurrentProcess().MainModule.FileName; - path.GetActualCasing().Should().Be(path); - path.GetActualCasing().Should().Be(path); - } + [Test] public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windows() diff --git a/NzbDrone.Common.Test/packages.config b/NzbDrone.Common.Test/packages.config index c03d2b5be..01e492eef 100644 --- a/NzbDrone.Common.Test/packages.config +++ b/NzbDrone.Common.Test/packages.config @@ -1,6 +1,6 @@  - + diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs index 062cd79d7..851ba4550 100644 --- a/NzbDrone.Common/DiskProvider.cs +++ b/NzbDrone.Common/DiskProvider.cs @@ -17,7 +17,6 @@ namespace NzbDrone.Common DateTime GetLastFolderWrite(string path); DateTime GetLastFileWrite(string path); void EnsureFolder(string path); - bool FolderExists(string path, bool caseSensitive); bool FolderExists(string path); bool FileExists(string path); bool FileExists(string path, bool caseSensitive); @@ -32,7 +31,7 @@ namespace NzbDrone.Common void MoveFile(string source, string destination); void DeleteFolder(string path, bool recursive); void InheritFolderPermissions(string filename); - long GetAvilableSpace(string path); + long? GetAvailableSpace(string path); string ReadAllText(string filePath); void WriteAllText(string filename, string contents); void FileSetLastWriteTimeUtc(string path, DateTime dateTime); @@ -113,16 +112,6 @@ namespace NzbDrone.Common return Directory.Exists(path); } - public bool FolderExists(string path, bool caseSensitive) - { - if (caseSensitive) - { - return FolderExists(path) && path == path.GetActualCasing(); - } - - return FolderExists(path); - } - public bool FileExists(string path) { Ensure.That(() => path).IsValidPath(); @@ -289,27 +278,38 @@ namespace NzbDrone.Common File.SetAccessControl(filename, fs); } - public long GetAvilableSpace(string path) + public long? GetAvailableSpace(string path) { Ensure.That(() => path).IsValidPath(); - if (OsInfo.IsLinux) - { - var driveInfo = DriveInfo.GetDrives().SingleOrDefault(c => c.IsReady && path.StartsWith(c.Name, StringComparison.CurrentCultureIgnoreCase)); - - if (driveInfo == null) - { - throw new DirectoryNotFoundException(path); - } - - return driveInfo.AvailableFreeSpace; - } - var root = GetPathRoot(path); if (!FolderExists(root)) throw new DirectoryNotFoundException(root); + if (OsInfo.IsLinux) + { + var drives = DriveInfo.GetDrives(); + + foreach (var drive in drives) + { + 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 null; + } + + return DriveFreeSpaceEx(root); } diff --git a/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs index 9a0275894..0cd595464 100644 --- a/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs +++ b/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs @@ -95,8 +95,7 @@ namespace NzbDrone.Common.EnsureThat return param; } - private static readonly Regex windowsInvalidPathRegex = new Regex(@"[/*<>""|]", RegexOptions.Compiled); - private static readonly Regex windowsPathRegex = new Regex(@"^[a-zA-Z]:\\", RegexOptions.Compiled); + [DebuggerStepThrough] public static Param IsValidPath(this Param param) @@ -104,31 +103,14 @@ namespace NzbDrone.Common.EnsureThat if (string.IsNullOrWhiteSpace(param.Value)) throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNullOrWhiteSpace); + if (param.Value.IsPathValid()) return param; + if (OsInfo.IsLinux) { - if (!param.Value.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid *nix path. paths must start with /", param.Value)); - } + 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)); - } - } - - - return param; + + throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid Windows path. paths must be a full path eg. C:\\Windows", param.Value)); } } } diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj index 42d7d0903..c1b17236f 100644 --- a/NzbDrone.Common/NzbDrone.Common.csproj +++ b/NzbDrone.Common/NzbDrone.Common.csproj @@ -93,6 +93,7 @@ + diff --git a/NzbDrone.Common/PathExtensions.cs b/NzbDrone.Common/PathExtensions.cs index 15e3b8a08..15687895f 100644 --- a/NzbDrone.Common/PathExtensions.cs +++ b/NzbDrone.Common/PathExtensions.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text.RegularExpressions; using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnvironmentInfo; @@ -45,6 +46,29 @@ namespace NzbDrone.Common return String.Equals(firstPath.CleanFilePath(), secondPath.CleanFilePath(), StringComparison.InvariantCultureIgnoreCase); } + private static readonly Regex WindowsPathWithDriveRegex = new Regex(@"^[a-zA-Z]:\\", RegexOptions.Compiled); + + public static bool IsPathValid(this string path) + { + if (path.ContainsInvalidPathChars() || string.IsNullOrWhiteSpace(path)) + { + return false; + } + + if (OsInfo.IsLinux) + { + return path.StartsWith(Path.DirectorySeparatorChar.ToString()); + } + + if (path.StartsWith("\\") || WindowsPathWithDriveRegex.IsMatch(path)) + { + return true; + } + + return false; + } + + public static bool ContainsInvalidPathChars(this string text) { return text.IndexOfAny(Path.GetInvalidPathChars()) >= 0; @@ -58,28 +82,40 @@ namespace NzbDrone.Common //Drive letter return dirInfo.Name.ToUpper(); } - return Path.Combine(GetProperCapitalization(parentDirInfo), parentDirInfo.GetDirectories(dirInfo.Name)[0].Name); + + var folderName = dirInfo.Name; + + if (dirInfo.Exists) + { + folderName = parentDirInfo.GetDirectories(dirInfo.Name)[0].Name; + } + + return Path.Combine(GetProperCapitalization(parentDirInfo), folderName); } public static string GetActualCasing(this string path) { - var attributes = File.GetAttributes(path); - if (OsInfo.IsLinux || path.StartsWith("\\")) { return path; } - if ((attributes & FileAttributes.Directory) == FileAttributes.Directory) + if (Directory.Exists(path) && (File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory) { return GetProperCapitalization(new DirectoryInfo(path)); } var fileInfo = new FileInfo(path); + var dirInfo = fileInfo.Directory; + var fileName = fileInfo.Name; - DirectoryInfo dirInfo = fileInfo.Directory; - return Path.Combine(GetProperCapitalization(dirInfo), dirInfo.GetFiles(fileInfo.Name)[0].Name); + if (dirInfo != null && fileInfo.Exists) + { + fileName = dirInfo.GetFiles(fileInfo.Name)[0].Name; + } + + return Path.Combine(GetProperCapitalization(dirInfo), fileName); } public static string GetAppDataPath(this IAppFolderInfo appFolderInfo) diff --git a/NzbDrone.Common/Serializer/Json.cs b/NzbDrone.Common/Serializer/Json.cs index 59c58946a..866776ec9 100644 --- a/NzbDrone.Common/Serializer/Json.cs +++ b/NzbDrone.Common/Serializer/Json.cs @@ -40,7 +40,6 @@ namespace NzbDrone.Common.Serializer return JsonConvert.SerializeObject(obj); } - public static void Serialize(TModel model, TextWriter outputStream) { var jsonTextWriter = new JsonTextWriter(outputStream); @@ -52,7 +51,5 @@ namespace NzbDrone.Common.Serializer { Serialize(model, new StreamWriter(outputStream)); } - - } } \ No newline at end of file diff --git a/NzbDrone.Common/Services.cs b/NzbDrone.Common/Services.cs new file mode 100644 index 000000000..0824c4735 --- /dev/null +++ b/NzbDrone.Common/Services.cs @@ -0,0 +1,15 @@ +using System; + +namespace NzbDrone.Common +{ + public class Services + { + public static String RootUrl + { + get + { + return "http://services.nzbdrone.com"; + } + } + } +} diff --git a/NzbDrone.Console/ConsoleApp.cs b/NzbDrone.Console/ConsoleApp.cs index 586027a54..3119cc811 100644 --- a/NzbDrone.Console/ConsoleApp.cs +++ b/NzbDrone.Console/ConsoleApp.cs @@ -1,5 +1,6 @@ using System; using System.Threading; +using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Host; @@ -18,7 +19,6 @@ namespace NzbDrone.Console } catch (Exception e) { - System.Console.WriteLine(e.ToString()); System.Console.ReadLine(); } diff --git a/NzbDrone.Core.Test/DataAugmentationFixture/Scene/SceneMappingProxyFixture.cs b/NzbDrone.Core.Test/DataAugmentationFixture/Scene/SceneMappingProxyFixture.cs index 64c5cbc79..98ad8b504 100644 --- a/NzbDrone.Core.Test/DataAugmentationFixture/Scene/SceneMappingProxyFixture.cs +++ b/NzbDrone.Core.Test/DataAugmentationFixture/Scene/SceneMappingProxyFixture.cs @@ -15,14 +15,6 @@ namespace NzbDrone.Core.Test.DataAugmentationFixture.Scene { private const string SCENE_MAPPING_URL = "http://services.nzbdrone.com/SceneMapping/Active"; - [SetUp] - public void Setup() - { - Mocker.GetMock().SetupGet(s => s.ServiceRootUrl) - .Returns("http://services.nzbdrone.com"); - - } - [Test] public void fetch_should_return_list_of_mappings() { diff --git a/NzbDrone.Core.Test/IndexerTests/IntegrationTests/IndexerIntegrationTests.cs b/NzbDrone.Core.Test/IndexerTests/IntegrationTests/IndexerIntegrationTests.cs index d052503a9..813c1a45f 100644 --- a/NzbDrone.Core.Test/IndexerTests/IntegrationTests/IndexerIntegrationTests.cs +++ b/NzbDrone.Core.Test/IndexerTests/IntegrationTests/IndexerIntegrationTests.cs @@ -44,17 +44,17 @@ namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests [Test] - [Explicit("needs newznab api key")] public void nzbsorg_rss() { var indexer = new Newznab(); indexer.Settings = new NewznabSettings { - ApiKey = "", + ApiKey = "64d61d3cfd4b75e51d01cbc7c6a78275", Url = "http://nzbs.org" }; indexer.InstanceDefinition = new IndexerDefinition(); + indexer.InstanceDefinition.Name = "nzbs.org"; var result = Subject.FetchRss(indexer); diff --git a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/FreeSpaceSpecificationFixture.cs b/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/FreeSpaceSpecificationFixture.cs index 7d2948ff8..47804bde0 100644 --- a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/FreeSpaceSpecificationFixture.cs +++ b/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/FreeSpaceSpecificationFixture.cs @@ -54,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests private void GivenFreeSpace(long size) { Mocker.GetMock() - .Setup(s => s.GetAvilableSpace(It.IsAny())) + .Setup(s => s.GetAvailableSpace(It.IsAny())) .Returns(size); } @@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); Mocker.GetMock() - .Verify(v => v.GetAvilableSpace(_rootFolder), Times.Once()); + .Verify(v => v.GetAvailableSpace(_rootFolder), Times.Once()); } } } diff --git a/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs b/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs index 9e34a5882..a7d13ff0c 100644 --- a/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs +++ b/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs @@ -53,8 +53,7 @@ namespace NzbDrone.Core.Test.MediaFileTests } Mocker.GetMock() - .Setup(s => s.UpgradeEpisodeFile(It.IsAny(), It.IsAny())) - .Returns(new EpisodeFile()); + .Setup(s => s.UpgradeEpisodeFile(It.IsAny(), It.IsAny())); } [Test] diff --git a/NzbDrone.Core.Test/MediaFileTests/RenameEpisodeFileServiceFixture.cs b/NzbDrone.Core.Test/MediaFileTests/RenameEpisodeFileServiceFixture.cs index dbdf35b56..80b4dc855 100644 --- a/NzbDrone.Core.Test/MediaFileTests/RenameEpisodeFileServiceFixture.cs +++ b/NzbDrone.Core.Test/MediaFileTests/RenameEpisodeFileServiceFixture.cs @@ -60,8 +60,7 @@ namespace NzbDrone.Core.Test.MediaFileTests private void GivenMovedFiles() { Mocker.GetMock() - .Setup(s => s.MoveEpisodeFile(It.IsAny(), _series)) - .Returns(_episodeFiles.First()); + .Setup(s => s.MoveEpisodeFile(It.IsAny(), _series)); } [Test] diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index b3e34350c..524d84b1d 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -43,9 +43,9 @@ ..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll - + False - ..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll + ..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll False @@ -186,6 +186,7 @@ + diff --git a/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index 30c728272..192ff7380 100644 --- a/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -353,6 +353,7 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("H.Polukatoikia.S03E13.Greek.PDTV.XviD-Ouzo", Language.Greek)] [TestCase("Burn.Notice.S04E15.Brotherly.Love.GERMAN.DUBBED.WS.WEBRiP.XviD.REPACK-TVP", Language.German)] [TestCase("Ray Donovan - S01E01.720p.HDtv.x264-Evolve (NLsub)", Language.Norwegian)] + [TestCase("Shield,.The.1x13.Tueurs.De.Flics.FR.DVDRip.XviD", Language.French)] public void parse_language(string postTitle, Language language) { var result = Parser.Parser.ParseTitle(postTitle); diff --git a/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs b/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs index 7f433c41e..a1a8c60f0 100644 --- a/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs +++ b/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs @@ -12,74 +12,118 @@ namespace NzbDrone.Core.Test.ParserTests 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[] { "WEEDS.S03E01-06.DUAL.BDRip.X-viD.AC3.-HELLYWOOD", Quality.DVD, false }, - new object[] { "WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", Quality.DVD, false }, - new object[] { "Two.and.a.Half.Men.S08E05.720p.HDTV.X264-DIMENSION", Quality.HDTV720p, false }, - new object[] { "Chuck S11E03 has no periods or extension HDTV", Quality.SDTV, false }, - new object[] { "Chuck.S04E05.HDTV.XviD-LOL", Quality.SDTV, false }, - new object[] { "The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", Quality.DVD, false }, - new object[] { "The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", Quality.DVD, false }, - new object[] { "The.Girls.Next.Door.S03E06.HDTV-WiDE", Quality.SDTV, false }, - new object[] { "Degrassi.S10E27.WS.DSR.XviD-2HD", Quality.SDTV, false }, - new object[] { "Sonny.With.a.Chance.S02E15.720p.WEB-DL.DD5.1.H.264-SURFER", Quality.WEBDL720p, false }, - new object[] { "Sonny.With.a.Chance.S02E15.720p", Quality.HDTV720p, false }, - new object[] { "Sonny.With.a.Chance.S02E15.mkv", Quality.HDTV720p, false }, - new object[] { "Sonny.With.a.Chance.S02E15.avi", Quality.SDTV, false }, - new object[] { "Sonny.With.a.Chance.S02E15.xvid", Quality.SDTV, false }, - new object[] { "Sonny.With.a.Chance.S02E15.divx", Quality.SDTV, false }, - new object[] { "Sonny.With.a.Chance.S02E15", Quality.Unknown, false }, - new object[] { "Chuck - S01E04 - So Old - Playdate - 720p TV.mkv", Quality.HDTV720p, false }, - new object[] { "Chuck - S22E03 - MoneyBART - HD TV.mkv", Quality.HDTV720p, false }, - new object[] { "Chuck - S01E03 - Come Fly With Me - 720p BluRay.mkv", Quality.Bluray720p, false }, - new object[] { "Chuck - S01E03 - Come Fly With Me - 1080p BluRay.mkv", Quality.Bluray1080p, false }, - new object[] { "Chuck - S11E06 - D-Yikes! - 720p WEB-DL.mkv", Quality.WEBDL720p, false }, - new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", Quality.DVD, false }, - new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", Quality.DVD, false }, - new object[] { "Law & Order: Special Victims Unit - 11x11 - Quickie", Quality.Unknown, false }, - new object[] { "S07E23 - [HDTV-720p].mkv ", Quality.HDTV720p, false }, - new object[] { "S07E23 - [WEBDL].mkv ", Quality.WEBDL720p, false }, - new object[] { "S07E23.mkv ", Quality.HDTV720p, false }, - new object[] { "S07E23 .avi ", Quality.SDTV, false }, - new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3.-HELLYWOOD.avi", Quality.DVD, false }, - new object[] { "WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", Quality.Bluray720p, false }, - new object[] { "The Voice S01E11 The Finals 1080i HDTV DD5.1 MPEG2-TrollHD", Quality.RAWHD, false }, - new object[] { "Nikita S02E01 HDTV XviD 2HD", Quality.SDTV, false }, - new object[] { "Gossip Girl S05E11 PROPER HDTV XviD 2HD", Quality.SDTV, true }, - new object[] { "The Jonathan Ross Show S02E08 HDTV x264 FTP", Quality.SDTV, false }, - new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-TLA", Quality.SDTV, false }, - new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA", Quality.SDTV, true }, - new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", Quality.DVD, true }, - new object[] { "Pawn Stars S04E87 REPACK 720p HDTV x264 aAF", Quality.HDTV720p, true }, - new object[] { "The Real Housewives of Vancouver S01E04 DSR x264 2HD", Quality.SDTV, false }, - new object[] { "Vanguard S01E04 Mexicos Death Train DSR x264 MiNDTHEGAP", Quality.SDTV, false }, - new object[] { "Vanguard S01E04 Mexicos Death Train 720p WEB DL", Quality.WEBDL720p, false }, - new object[] { "Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", Quality.WEBDL720p, false }, - new object[] { "Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", Quality.WEBDL720p, false }, - new object[] { "Fringe S04E22 720p WEB-DL DD5.1 H264-EbP.mkv", Quality.WEBDL720p, false }, - new object[] { "CSI NY S09E03 1080p WEB DL DD5 1 H264 NFHD", Quality.WEBDL1080p, false }, - new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 NFHD", Quality.WEBDL1080p, false }, - new object[] { "Criminal.Minds.S08E01.1080p.WEB-DL.DD5.1.H264-NFHD", Quality.WEBDL1080p, false }, - new object[] { "Its.Always.Sunny.in.Philadelphia.S08E01.1080p.WEB-DL.proper.AAC2.0.H.264", Quality.WEBDL1080p, true }, - new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 REPACK NFHD", Quality.WEBDL1080p, true }, - new object[] { "Glee.S04E09.Swan.Song.1080p.WEB-DL.DD5.1.H.264-ECI", Quality.WEBDL1080p, false }, - new object[] { "Elementary.S01E10.The.Leviathan.480p.WEB-DL.x264-mSD", Quality.WEBDL480p, false }, - new object[] { "Glee.S04E10.Glee.Actually.480p.WEB-DL.x264-mSD", Quality.WEBDL480p, false }, - new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.480p.WEB-DL.x264-mSD", Quality.WEBDL480p, false }, - new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.1080p.WEB-DL.DD5.1.H.264", Quality.WEBDL1080p, false }, - new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.X264-QCF", Quality.HDTV1080p, false }, - new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.x264-QCF", Quality.HDTV1080p, false }, - new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.proper.X264-QCF", Quality.HDTV1080p, true }, - new object[] { "Dexter - S01E01 - Title [HDTV]", Quality.HDTV720p, false }, - new object[] { "Dexter - S01E01 - Title [HDTV-720p]", Quality.HDTV720p, false }, - new object[] { "Dexter - S01E01 - Title [HDTV-1080p]", Quality.HDTV1080p, false }, - new object[] { "POI S02E11 1080i HDTV DD5.1 MPEG2-TrollHD", Quality.RAWHD, false }, - new object[] { "How I Met Your Mother S01E18 Nothing Good Happens After 2 A.M. 720p HDTV DD5.1 MPEG2-TrollHD", Quality.RAWHD, false }, - new object[] { "Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", Quality.WEBDL1080p, false }, - new object[] { "Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", Quality.WEBDL720p, false }, - new object[] { "Sons.Of.Anarchy.S02E13.1080p.BluRay.x264-AVCDVD", Quality.Bluray1080p, false } + new object[] { "S07E23 .avi ", false }, + new object[] {"The.Shield.S01E13.x264-CtrlSD", false}, + new object[] { "Nikita S02E01 HDTV XviD 2HD", false }, + new object[] { "Gossip Girl S05E11 PROPER HDTV XviD 2HD", true }, + new object[] { "The Jonathan Ross Show S02E08 HDTV x264 FTP", false }, + new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-TLA", false }, + new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA", true }, + new object[] { "The Real Housewives of Vancouver S01E04 DSR x264 2HD", false }, + new object[] { "Vanguard S01E04 Mexicos Death Train DSR x264 MiNDTHEGAP", false }, + new object[] { "Chuck S11E03 has no periods or extension HDTV", false }, + new object[] { "Chuck.S04E05.HDTV.XviD-LOL", false }, + new object[] { "Sonny.With.a.Chance.S02E15.avi", false }, + new object[] { "Sonny.With.a.Chance.S02E15.xvid", false }, + new object[] { "Sonny.With.a.Chance.S02E15.divx", false }, + new object[] { "The.Girls.Next.Door.S03E06.HDTV-WiDE", false }, + new object[] { "Degrassi.S10E27.WS.DSR.XviD-2HD", false }, + }; + + public static object[] DvdCases = + { + new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", true }, + new object[] { "The.Shield.S01E13.NTSC.x264-CtrlSD", false }, + new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", false }, + new object[] { "WEEDS.S03E01-06.DUAL.BDRip.X-viD.AC3.-HELLYWOOD", false }, + new object[] { "WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", false }, + new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false }, + new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false }, + new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3.-HELLYWOOD.avi", false }, + new object[] { "The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", false }, + new object[] { "The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", false }, + new object[] { "the.shield.1x13.circles.ws.xvidvd-tns", false} + }; + + public static object[] Webdl480pCases = + { + new object[] { "Elementary.S01E10.The.Leviathan.480p.WEB-DL.x264-mSD", false }, + new object[] { "Glee.S04E10.Glee.Actually.480p.WEB-DL.x264-mSD", false }, + new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.480p.WEB-DL.x264-mSD", false }, + }; + + public static object[] Hdtv720pCases = + { + new object[] { "Dexter - S01E01 - Title [HDTV]", false }, + new object[] { "Dexter - S01E01 - Title [HDTV-720p]", false }, + new object[] { "Pawn Stars S04E87 REPACK 720p HDTV x264 aAF", true }, + new object[] { "Sonny.With.a.Chance.S02E15.720p", false }, + new object[] { "S07E23 - [HDTV-720p].mkv ", false }, + new object[] { "Chuck - S22E03 - MoneyBART - HD TV.mkv", false }, + new object[] { "S07E23.mkv ", false }, + new object[] { "Two.and.a.Half.Men.S08E05.720p.HDTV.X264-DIMENSION", false }, + new object[] { "Sonny.With.a.Chance.S02E15.mkv", false }, + }; + + public static object[] Hdtv1080pCases = + { + new object[] { "Under the Dome S01E10 Let the Games Begin 1080p", false }, + new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.X264-QCF", false }, + new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.x264-QCF", false }, + new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.proper.X264-QCF", true }, + new object[] { "Dexter - S01E01 - Title [HDTV-1080p]", false }, + }; + + public static object[] Webdl720pCases = + { + new object[] { "Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", false }, + new object[] { "Vanguard S01E04 Mexicos Death Train 720p WEB DL", false }, + new object[] { "Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", false }, + new object[] { "Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", false }, + new object[] { "Chuck - S11E06 - D-Yikes! - 720p WEB-DL.mkv", false }, + new object[] { "Sonny.With.a.Chance.S02E15.720p.WEB-DL.DD5.1.H.264-SURFER", false }, + new object[] { "S07E23 - [WEBDL].mkv ", false }, + new object[] { "Fringe S04E22 720p WEB-DL DD5.1 H264-EbP.mkv", false }, + }; + + public static object[] Webdl1080pCases = + { + new object[] { "Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false }, + new object[] { "CSI NY S09E03 1080p WEB DL DD5 1 H264 NFHD", false }, + new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 NFHD", false }, + new object[] { "Criminal.Minds.S08E01.1080p.WEB-DL.DD5.1.H264-NFHD", false }, + new object[] { "Its.Always.Sunny.in.Philadelphia.S08E01.1080p.WEB-DL.proper.AAC2.0.H.264", true }, + new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 REPACK NFHD", true }, + new object[] { "Glee.S04E09.Swan.Song.1080p.WEB-DL.DD5.1.H.264-ECI", false }, + new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.1080p.WEB-DL.DD5.1.H.264", false }, + }; + + public static object[] Bluray720pCases = + { + new object[] { "WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", false }, + new object[] { "Chuck - S01E03 - Come Fly With Me - 720p BluRay.mkv", false }, + }; + + public static object[] Bluray1080pCases = + { + new object[] { "Chuck - S01E03 - Come Fly With Me - 1080p BluRay.mkv", false }, + new object[] { "Sons.Of.Anarchy.S02E13.1080p.BluRay.x264-AVCDVD", false }, + }; + + public static object[] RawCases = + { + new object[] { "POI S02E11 1080i HDTV DD5.1 MPEG2-TrollHD", false }, + new object[] { "How I Met Your Mother S01E18 Nothing Good Happens After 2 A.M. 720p HDTV DD5.1 MPEG2-TrollHD", false }, + new object[] { "The Voice S01E11 The Finals 1080i HDTV DD5.1 MPEG2-TrollHD", false }, + }; + + public static object[] UnknownCases = + { + new object[] { "Sonny.With.a.Chance.S02E15", false }, + new object[] { "Law & Order: Special Victims Unit - 11x11 - Quickie", false }, + }; public static object[] SelfQualityParserCases = @@ -94,20 +138,100 @@ namespace NzbDrone.Core.Test.ParserTests new object[] { Quality.Bluray1080p } }; - [Test, TestCaseSource("QualityParserCases")] - public void quality_parse(string postTitle, Quality quality, bool proper) + [Test, TestCaseSource("SdtvCases")] + public void should_parse_sdtv_quality(string postTitle, bool proper) { - var result = Parser.Parser.ParseTitle(postTitle); - result.Quality.Quality.Should().Be(quality); - result.Quality.Proper.Should().Be(proper); + var result = Parser.QualityParser.ParseQuality(postTitle); + result.Quality.Should().Be(Quality.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.QualityParser.ParseQuality(postTitle); + result.Quality.Should().Be(Quality.Unknown); + result.Proper.Should().Be(proper); } [Test, TestCaseSource("SelfQualityParserCases")] public void parsing_our_own_quality_enum(Quality quality) { var fileName = String.Format("My series S01E01 [{0}]", quality); - var result = Parser.Parser.ParseTitle(fileName); - result.Quality.Quality.Should().Be(quality); + var result = Parser.QualityParser.ParseQuality(fileName); + result.Quality.Should().Be(quality); } } } diff --git a/NzbDrone.Core.Test/ProviderTests/DiskProviderTests/FreeDiskSpaceFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskProviderTests/FreeDiskSpaceFixture.cs index 4584770ca..849cf820a 100644 --- a/NzbDrone.Core.Test/ProviderTests/DiskProviderTests/FreeDiskSpaceFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/DiskProviderTests/FreeDiskSpaceFixture.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests [Test] public void should_return_free_disk_space() { - var result = Subject.GetAvilableSpace(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); + var result = Subject.GetAvailableSpace(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); result.Should().BeGreaterThan(0); } @@ -23,7 +23,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests { WindowsOnly(); - var result = Subject.GetAvilableSpace(@"\\localhost\c$\Windows"); + var result = Subject.GetAvailableSpace(@"\\localhost\c$\Windows"); result.Should().BeGreaterThan(0); } @@ -32,13 +32,13 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests { WindowsOnly(); - Assert.Throws(() => Subject.GetAvilableSpace(@"Z:\NOT_A_REAL_PATH\DOES_NOT_EXIST".AsOsAgnostic())); + Assert.Throws(() => Subject.GetAvailableSpace(@"Z:\NOT_A_REAL_PATH\DOES_NOT_EXIST".AsOsAgnostic())); } [Test] public void should_be_able_to_get_space_on_folder_that_doesnt_exist() { - var result = Subject.GetAvilableSpace(@"C:\I_DO_NOT_EXIST".AsOsAgnostic()); + var result = Subject.GetAvailableSpace(@"C:\I_DO_NOT_EXIST".AsOsAgnostic()); result.Should().BeGreaterThan(0); } } diff --git a/NzbDrone.Core.Test/RootFolderTests/FreeSpaceOnDrivesFixture.cs b/NzbDrone.Core.Test/RootFolderTests/FreeSpaceOnDrivesFixture.cs index ff3458ab1..ec5ade721 100644 --- a/NzbDrone.Core.Test/RootFolderTests/FreeSpaceOnDrivesFixture.cs +++ b/NzbDrone.Core.Test/RootFolderTests/FreeSpaceOnDrivesFixture.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.RootFolderTests .Returns(@"C:\"); Mocker.GetMock() - .Setup(s => s.GetAvilableSpace(@"C:\")) + .Setup(s => s.GetAvailableSpace(@"C:\")) .Returns(123456); var result = Subject.FreeSpaceOnDrives(); @@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.RootFolderTests .Returns(@"C:\"); Mocker.GetMock() - .Setup(s => s.GetAvilableSpace(@"C:\")) + .Setup(s => s.GetAvailableSpace(@"C:\")) .Returns(123456); var result = Subject.FreeSpaceOnDrives(); @@ -76,7 +76,7 @@ namespace NzbDrone.Core.Test.RootFolderTests .Returns(@"D:\"); Mocker.GetMock() - .Setup(s => s.GetAvilableSpace(It.IsAny())) + .Setup(s => s.GetAvailableSpace(It.IsAny())) .Returns(123456); var result = Subject.FreeSpaceOnDrives(); @@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.RootFolderTests .Returns(@"C:\"); Mocker.GetMock() - .Setup(s => s.GetAvilableSpace(It.IsAny())) + .Setup(s => s.GetAvailableSpace(It.IsAny())) .Throws(new DirectoryNotFoundException()); var result = Subject.FreeSpaceOnDrives(); diff --git a/NzbDrone.Core.Test/TvTests/SeasonServiceTests/HandleEpisodeInfoDeletedEventFixture.cs b/NzbDrone.Core.Test/TvTests/SeasonServiceTests/HandleEpisodeInfoDeletedEventFixture.cs new file mode 100644 index 000000000..d31c2b679 --- /dev/null +++ b/NzbDrone.Core.Test/TvTests/SeasonServiceTests/HandleEpisodeInfoDeletedEventFixture.cs @@ -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 + { + private List _seasons; + private List _episodes; + + [SetUp] + public void Setup() + { + _seasons = Builder + .CreateListOfSize(1) + .All() + .With(s => s.SeriesId = 1) + .Build() + .ToList(); + + _episodes = Builder + .CreateListOfSize(1) + .All() + .With(e => e.SeasonNumber = _seasons.First().SeasonNumber) + .With(s => s.SeriesId = _seasons.First().SeasonNumber) + .Build() + .ToList(); + + Mocker.GetMock() + .Setup(s => s.GetSeasonBySeries(It.IsAny())) + .Returns(_seasons); + + Mocker.GetMock() + .Setup(s => s.GetEpisodesBySeason(It.IsAny(), _seasons.First().SeasonNumber)) + .Returns(_episodes); + } + + private void GivenAbandonedSeason() + { + Mocker.GetMock() + .Setup(s => s.GetEpisodesBySeason(It.IsAny(), _seasons.First().SeasonNumber)) + .Returns(new List()); + } + + [Test] + public void should_not_delete_when_season_is_still_valid() + { + Subject.Handle(new EpisodeInfoDeletedEvent(_episodes)); + + Mocker.GetMock() + .Verify(v => v.Delete(It.IsAny()), Times.Never()); + } + + [Test] + public void should_delete_season_if_no_episodes_exist_in_that_season() + { + GivenAbandonedSeason(); + + Subject.Handle(new EpisodeInfoDeletedEvent(_episodes)); + + Mocker.GetMock() + .Verify(v => v.Delete(It.IsAny()), Times.Once()); + } + + [Test] + public void should_only_delete_a_season_once() + { + _episodes = Builder + .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() + .Verify(v => v.Delete(It.IsAny()), Times.Once()); + } + } +} diff --git a/NzbDrone.Core.Test/UpdateTests/UpdatePackageProviderFixture.cs b/NzbDrone.Core.Test/UpdateTests/UpdatePackageProviderFixture.cs index bbf0c5b9c..138e3b404 100644 --- a/NzbDrone.Core.Test/UpdateTests/UpdatePackageProviderFixture.cs +++ b/NzbDrone.Core.Test/UpdateTests/UpdatePackageProviderFixture.cs @@ -16,13 +16,7 @@ namespace NzbDrone.Core.Test.UpdateTests Mocker.GetMock().SetupGet(c => c.Branch).Returns("master"); - var updates = Subject.GetAvailablePackages().ToList(); - - updates.Should().NotBeEmpty(); - updates.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.FileName)); - updates.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.Url)); - updates.Should().OnlyContain(c => c.Version != null); - updates.Should().OnlyContain(c => c.Version.Major == 2); + Subject.GetLatestUpdate().Should().BeNull(); } } } diff --git a/NzbDrone.Core.Test/packages.config b/NzbDrone.Core.Test/packages.config index 0881c7fe5..b751901cd 100644 --- a/NzbDrone.Core.Test/packages.config +++ b/NzbDrone.Core.Test/packages.config @@ -2,7 +2,7 @@ - + diff --git a/NzbDrone.Core/Configuration/ConfigService.cs b/NzbDrone.Core/Configuration/ConfigService.cs index 0d9189fc6..e3ca0317b 100644 --- a/NzbDrone.Core/Configuration/ConfigService.cs +++ b/NzbDrone.Core/Configuration/ConfigService.cs @@ -172,11 +172,6 @@ namespace NzbDrone.Core.Configuration set { SetValue("BlackholeFolder", value); } } - public string ServiceRootUrl - { - get { return "http://services.nzbdrone.com"; } - } - public string PneumaticFolder { get { return GetValue("PneumaticFolder", String.Empty); } diff --git a/NzbDrone.Core/Configuration/IConfigService.cs b/NzbDrone.Core/Configuration/IConfigService.cs index 07e70d25b..d84ed272b 100644 --- a/NzbDrone.Core/Configuration/IConfigService.cs +++ b/NzbDrone.Core/Configuration/IConfigService.cs @@ -25,7 +25,6 @@ namespace NzbDrone.Core.Configuration int Retention { get; set; } DownloadClientType DownloadClient { get; set; } string BlackholeFolder { get; set; } - string ServiceRootUrl { get; } string PneumaticFolder { get; set; } string RecycleBin { get; set; } String NzbgetUsername { get; set; } diff --git a/NzbDrone.Core/DataAugmentation/DailySeries/DailySeriesDataProxy.cs b/NzbDrone.Core/DataAugmentation/DailySeries/DailySeriesDataProxy.cs index fc2a591a2..f59ff9ee9 100644 --- a/NzbDrone.Core/DataAugmentation/DailySeries/DailySeriesDataProxy.cs +++ b/NzbDrone.Core/DataAugmentation/DailySeries/DailySeriesDataProxy.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using NLog; using NzbDrone.Common; using NzbDrone.Common.Serializer; -using NzbDrone.Core.Configuration; namespace NzbDrone.Core.DataAugmentation.DailySeries { @@ -17,13 +16,11 @@ namespace NzbDrone.Core.DataAugmentation.DailySeries public class DailySeriesDataProxy : IDailySeriesDataProxy { private readonly IHttpProvider _httpProvider; - private readonly IConfigService _configService; private readonly Logger _logger; - public DailySeriesDataProxy(IHttpProvider httpProvider, IConfigService configService, Logger logger) + public DailySeriesDataProxy(IHttpProvider httpProvider, Logger logger) { _httpProvider = httpProvider; - _configService = configService; _logger = logger; } @@ -31,7 +28,7 @@ namespace NzbDrone.Core.DataAugmentation.DailySeries { try { - var dailySeriesIds = _httpProvider.DownloadString(_configService.ServiceRootUrl + "/DailySeries/AllIds"); + var dailySeriesIds = _httpProvider.DownloadString(Services.RootUrl + "/DailySeries/AllIds"); var seriesIds = Json.Deserialize>(dailySeriesIds); @@ -49,7 +46,7 @@ namespace NzbDrone.Core.DataAugmentation.DailySeries { try { - var result = _httpProvider.DownloadString(_configService.ServiceRootUrl + "/DailySeries/Check?seriesId=" + tvdbid); + var result = _httpProvider.DownloadString(Services.RootUrl + "/DailySeries/Check?seriesId=" + tvdbid); return Convert.ToBoolean(result); } catch (Exception ex) diff --git a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProxy.cs b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProxy.cs index c38cb4a58..2447a96ec 100644 --- a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProxy.cs +++ b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProxy.cs @@ -13,19 +13,15 @@ namespace NzbDrone.Core.DataAugmentation.Scene public class SceneMappingProxy : ISceneMappingProxy { private readonly IHttpProvider _httpProvider; - private readonly IConfigService _configService; - - public SceneMappingProxy(IHttpProvider httpProvider, IConfigService configService) + public SceneMappingProxy(IHttpProvider httpProvider) { _httpProvider = httpProvider; - _configService = configService; - } public List Fetch() { - var mappingsJson = _httpProvider.DownloadString(_configService.ServiceRootUrl + "/SceneMapping/Active"); + var mappingsJson = _httpProvider.DownloadString(Services.RootUrl + "/SceneMapping/Active"); return Json.Deserialize>(mappingsJson); } } diff --git a/NzbDrone.Core/Datastore/Migration/016_updated_imported_history_item.cs b/NzbDrone.Core/Datastore/Migration/016_updated_imported_history_item.cs new file mode 100644 index 000000000..7a2c50e71 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/016_updated_imported_history_item.cs @@ -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"); + } + } +} diff --git a/NzbDrone.Core/Datastore/Migration/017_reset_scene_names.cs b/NzbDrone.Core/Datastore/Migration/017_reset_scene_names.cs new file mode 100644 index 000000000..e2e3a21d6 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/017_reset_scene_names.cs @@ -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"); + } + } +} diff --git a/NzbDrone.Core/History/HistoryService.cs b/NzbDrone.Core/History/HistoryService.cs index 40f637fcf..c0752eb2a 100644 --- a/NzbDrone.Core/History/HistoryService.cs +++ b/NzbDrone.Core/History/HistoryService.cs @@ -62,7 +62,7 @@ namespace NzbDrone.Core.History { var history = new History { - EventType = HistoryEventType.Grabbed, + EventType = HistoryEventType.Grabbed, Date = DateTime.UtcNow, Quality = message.Episode.ParsedEpisodeInfo.Quality, SourceTitle = message.Episode.Report.Title, @@ -81,20 +81,22 @@ namespace NzbDrone.Core.History public void Handle(EpisodeImportedEvent message) { - foreach (var episode in message.EpisodeFile.Episodes.Value) + foreach (var episode in message.DroppedEpisode.Episodes) { var history = new History { EventType = HistoryEventType.DownloadFolderImported, Date = DateTime.UtcNow, - Quality = message.EpisodeFile.Quality, - SourceTitle = message.EpisodeFile.Path, - SeriesId = message.EpisodeFile.SeriesId, + Quality = message.DroppedEpisode.Quality, + SourceTitle = message.ImportedEpisode.SceneName, + SeriesId = message.ImportedEpisode.SeriesId, EpisodeId = episode.Id }; - history.Data.Add("Path", message.EpisodeFile.Path); - history.Data.Add("Filename", Path.GetFileNameWithoutExtension(message.EpisodeFile.Path)); + //Won't have a value since we publish this event before saving to DB. + //history.Data.Add("FileId", message.ImportedEpisode.Id.ToString()); + history.Data.Add("DroppedPath", message.DroppedEpisode.Path); + history.Data.Add("ImportedPath", message.ImportedEpisode.Path); _historyRepository.Insert(history); } diff --git a/NzbDrone.Core/Indexers/BasicRssParser.cs b/NzbDrone.Core/Indexers/BasicRssParser.cs index d29889248..84c61f296 100644 --- a/NzbDrone.Core/Indexers/BasicRssParser.cs +++ b/NzbDrone.Core/Indexers/BasicRssParser.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using System.Xml; using System.Xml.Linq; using NLog; using NzbDrone.Core.Parser.Model; @@ -12,7 +13,7 @@ namespace NzbDrone.Core.Indexers { public interface IParseFeed { - IEnumerable Process(Stream source, string url); + IEnumerable Process(string xml, string url); } public class BasicRssParser : IParseFeed @@ -24,34 +25,37 @@ namespace NzbDrone.Core.Indexers _logger = LogManager.GetCurrentClassLogger(); } - public IEnumerable Process(Stream source, string url) + public IEnumerable Process(string xml, string url) { - var document = XDocument.Load(source); - var items = document.Descendants("item"); - - var result = new List(); - - foreach (var item in items) + using (var xmlTextReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { ProhibitDtd = false, IgnoreComments = true })) { - try - { - var reportInfo = ParseFeedItem(item); - if (reportInfo != null) - { - reportInfo.NzbUrl = GetNzbUrl(item); - reportInfo.NzbInfoUrl = GetNzbInfoUrl(item); + var document = XDocument.Load(xmlTextReader); + var items = document.Descendants("item"); - result.Add(reportInfo); + var result = new List(); + + foreach (var item in items) + { + try + { + var reportInfo = ParseFeedItem(item); + if (reportInfo != null) + { + reportInfo.NzbUrl = GetNzbUrl(item); + reportInfo.NzbInfoUrl = GetNzbInfoUrl(item); + + result.Add(reportInfo); + } + } + catch (Exception itemEx) + { + itemEx.Data.Add("Item", item.Title()); + _logger.ErrorException("An error occurred while processing feed item from " + url, itemEx); } } - catch (Exception itemEx) - { - itemEx.Data.Add("Item", item.Title()); - _logger.ErrorException("An error occurred while processing feed item from " + url, itemEx); - } - } - return result; + return result; + } } diff --git a/NzbDrone.Core/Indexers/IndexerFetchService.cs b/NzbDrone.Core/Indexers/IndexerFetchService.cs index 6c46032b6..2e585f862 100644 --- a/NzbDrone.Core/Indexers/IndexerFetchService.cs +++ b/NzbDrone.Core/Indexers/IndexerFetchService.cs @@ -106,19 +106,26 @@ namespace NzbDrone.Core.Indexers try { _logger.Trace("Downloading Feed " + url); - var stream = _httpProvider.DownloadStream(url); - result.AddRange(indexer.Parser.Process(stream, url)); + var xml = _httpProvider.DownloadString(url); + if (!string.IsNullOrWhiteSpace(xml)) + { + result.AddRange(indexer.Parser.Process(xml, url)); + } + else + { + _logger.Warn("{0} returned empty response.", url); + } + } catch (WebException webException) { - if (webException.Message.Contains("503") || webException.Message.Contains("timed out")) + if (webException.Message.Contains("502") || webException.Message.Contains("503") || webException.Message.Contains("timed out")) { _logger.Warn("{0} server is currently unavailable. {1} {2}", indexer.Name, url, webException.Message); } else { - webException.Data.Add("FeedUrl", url); - _logger.WarnException("An error occurred while processing feed. " + url, webException); + _logger.Warn("{0} {1} {2}", indexer.Name, url, webException.Message); } } catch (Exception feedEx) diff --git a/NzbDrone.Core/Instrumentation/LogRepository.cs b/NzbDrone.Core/Instrumentation/LogRepository.cs index 5fe755580..0a00aa27b 100644 --- a/NzbDrone.Core/Instrumentation/LogRepository.cs +++ b/NzbDrone.Core/Instrumentation/LogRepository.cs @@ -18,7 +18,7 @@ namespace NzbDrone.Core.Instrumentation public void Trim() { - var trimDate = DateTime.UtcNow.AddDays(-15).Date; + var trimDate = DateTime.UtcNow.AddDays(-7).Date; Delete(c => c.Time <= trimDate); } } diff --git a/NzbDrone.Core/MediaCover/MediaCoverService.cs b/NzbDrone.Core/MediaCover/MediaCoverService.cs index 99f10e4e7..e3d247bfa 100644 --- a/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -44,30 +44,31 @@ namespace NzbDrone.Core.MediaCover foreach (var cover in series.Images) { var fileName = GetCoverPath(series.Id, cover.CoverType); - if (!_coverExistsSpecification.AlreadyExists(cover.Url, fileName)) + try { - DownloadCover(series, cover); + if (!_coverExistsSpecification.AlreadyExists(cover.Url, fileName)) + { + DownloadCover(series, cover); + } + } + catch (WebException e) + { + _logger.Warn(string.Format("Couldn't download media cover for {0}. {1}", series, e.Message)); + } + catch (Exception e) + { + _logger.ErrorException("Couldn't download media cover for " + series, e); } } } private void DownloadCover(Series series, MediaCover cover) { - try - { - var fileName = GetCoverPath(series.Id, cover.CoverType); + var fileName = GetCoverPath(series.Id, cover.CoverType); + + _logger.Info("Downloading {0} for {1} {2}", cover.CoverType, series, cover.Url); + _httpProvider.DownloadFile(cover.Url, fileName); - _logger.Info("Downloading {0} for {1} {2}", cover.CoverType, series, cover.Url); - _httpProvider.DownloadFile(cover.Url, fileName); - } - catch (WebException e) - { - _logger.Warn("Couldn't download media cover for " + series); - } - catch (Exception e) - { - _logger.ErrorException("Couldn't download media cover for " + series, e); - } } public void HandleAsync(SeriesDeletedEvent message) diff --git a/NzbDrone.Core/MediaFiles/DiskScanService.cs b/NzbDrone.Core/MediaFiles/DiskScanService.cs index 17d7e6dc2..0fb6901fe 100644 --- a/NzbDrone.Core/MediaFiles/DiskScanService.cs +++ b/NzbDrone.Core/MediaFiles/DiskScanService.cs @@ -24,9 +24,9 @@ namespace NzbDrone.Core.MediaFiles private const string EXTENSIONS = //XBMC - ".m4v .3gp .nsv .ts .ty .strm .rm .rmvb .m3u .ifo .mov .qt .divx .xvid .bivx .vob .nrg .img" + + ".m4v .3gp .nsv .ts .ty .strm .rm .rmvb .m3u .ifo .mov .qt .divx .xvid .bivx .vob .nrg .img " + ".iso .pva .wmv .asf .asx .ogm .m2v .avi .bin .dat .dvr-ms .mpg .mpeg .mp4 .mkv .avc .vp3 " + - ".svq3 .nuv .viv .dv .fli .flv .wpl" + + ".svq3 .nuv .viv .dv .fli .flv .wpl " + //Other ".m2ts"; diff --git a/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs b/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs index e46bf3b26..ef230443b 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs @@ -13,8 +13,8 @@ namespace NzbDrone.Core.MediaFiles { public interface IMoveEpisodeFiles { - EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series); - EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode); + string MoveEpisodeFile(EpisodeFile episodeFile, Series series); + string MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode); } public class MoveEpisodeFiles : IMoveEpisodeFiles @@ -38,27 +38,26 @@ namespace NzbDrone.Core.MediaFiles _logger = logger; } - public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series) + public string MoveEpisodeFile(EpisodeFile episodeFile, Series series) { var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id); var newFileName = _buildFileNames.BuildFilename(episodes, series, episodeFile); - var destinationFilename = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path)); + var filePath = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path)); + MoveFile(episodeFile, filePath); - return MoveFile(episodeFile, destinationFilename); + return filePath; } - public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode) + public string MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode) { var newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile); - var destinationFilename = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path)); - episodeFile = MoveFile(episodeFile, destinationFilename); + var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path)); + MoveFile(episodeFile, filePath); - _messageAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode)); - - return episodeFile; + return filePath; } - private EpisodeFile MoveFile(EpisodeFile episodeFile, string destinationFilename) + private void MoveFile(EpisodeFile episodeFile, string destinationFilename) { if (!_diskProvider.FileExists(episodeFile.Path)) { @@ -85,10 +84,6 @@ namespace NzbDrone.Core.MediaFiles _logger.Debug("Unable to apply folder permissions to: ", destinationFilename); _logger.TraceException(ex.Message, ex); } - - episodeFile.Path = destinationFilename; - - return episodeFile; } } } \ No newline at end of file diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs index 0757b1495..733b8580d 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport { List Import(List decisions, bool newDownloads = false); } - + public class ImportApprovedEpisodes : IImportApprovedEpisodes { private readonly IUpgradeMediaFiles _episodeFileUpgrader; @@ -62,15 +62,17 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path); episodeFile.Quality = localEpisode.Quality; episodeFile.SeasonNumber = localEpisode.SeasonNumber; - episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath()); episodeFile.Episodes = localEpisode.Episodes; + if (newDownload) { - episodeFile = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode); - _messageAggregator.PublishEvent(new EpisodeImportedEvent(episodeFile)); + episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath()); + episodeFile.Path = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode); + _messageAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile)); + _messageAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode)); } - + _mediaFileService.Add(episodeFile); imported.Add(importDecision); } diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs index 833e368a3..9c499d8fb 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs @@ -24,7 +24,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications try { var path = Directory.GetParent(localEpisode.Series.Path); - var freeSpace = _diskProvider.GetAvilableSpace(path.FullName); + var freeSpace = _diskProvider.GetAvailableSpace(path.FullName); if (freeSpace < localEpisode.Size + 100.Megabytes()) { diff --git a/NzbDrone.Core/MediaFiles/Events/EpisodeImportedEvent.cs b/NzbDrone.Core/MediaFiles/Events/EpisodeImportedEvent.cs index 9c8e59cdc..2f166b069 100644 --- a/NzbDrone.Core/MediaFiles/Events/EpisodeImportedEvent.cs +++ b/NzbDrone.Core/MediaFiles/Events/EpisodeImportedEvent.cs @@ -1,14 +1,17 @@ using NzbDrone.Common.Messaging; +using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.Events { public class EpisodeImportedEvent : IEvent { - public EpisodeFile EpisodeFile { get; private set; } + public LocalEpisode DroppedEpisode { get; private set; } + public EpisodeFile ImportedEpisode { get; private set; } - public EpisodeImportedEvent(EpisodeFile episodeFile) + public EpisodeImportedEvent(LocalEpisode droppedEpisode, EpisodeFile importedEpisode) { - EpisodeFile = episodeFile; + DroppedEpisode = droppedEpisode; + ImportedEpisode = importedEpisode; } } } \ No newline at end of file diff --git a/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs b/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs index f6737a9ef..7912039e7 100644 --- a/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs +++ b/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs @@ -34,14 +34,12 @@ namespace NzbDrone.Core.MediaFiles { var renamed = new List(); - foreach (var file in episodeFiles) + foreach (var episodeFile in episodeFiles) { try { - var episodeFile = file; - _logger.Trace("Renaming episode file: {0}", episodeFile); - episodeFile = _episodeFileMover.MoveEpisodeFile(episodeFile, series); + episodeFile.Path = _episodeFileMover.MoveEpisodeFile(episodeFile, series); _mediaFileService.Update(episodeFile); renamed.Add(episodeFile); @@ -54,7 +52,7 @@ namespace NzbDrone.Core.MediaFiles } catch (Exception ex) { - _logger.ErrorException("Failed to rename file: " + file.Path, ex); + _logger.ErrorException("Failed to rename file: " + episodeFile.Path, ex); } } diff --git a/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs b/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs index c272e9197..75ee6869e 100644 --- a/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs +++ b/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.MediaFiles { public interface IUpgradeMediaFiles { - EpisodeFile UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode); + string UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode); } public class UpgradeMediaFileService : IUpgradeMediaFiles @@ -31,7 +31,7 @@ namespace NzbDrone.Core.MediaFiles _logger = logger; } - public EpisodeFile UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode) + public string UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode) { var existingFiles = localEpisode.Episodes .Where(e => e.EpisodeFileId > 0) diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 15ded7729..3f95a1ab6 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -158,6 +158,8 @@ + + @@ -342,6 +344,7 @@ + @@ -350,6 +353,7 @@ + @@ -493,6 +497,7 @@ + diff --git a/NzbDrone.Core/Parser/Model/RemoteEpisode.cs b/NzbDrone.Core/Parser/Model/RemoteEpisode.cs index cc7459e4f..70b001511 100644 --- a/NzbDrone.Core/Parser/Model/RemoteEpisode.cs +++ b/NzbDrone.Core/Parser/Model/RemoteEpisode.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Core.Parser.Model public bool IsRecentEpisode() { - return Episodes.Any(e => e.AirDateUtc >= DateTime.Today.AddDays(-14)); + return Episodes.Any(e => e.AirDateUtc >= DateTime.UtcNow.Date.AddDays(-14)); } public override string ToString() diff --git a/NzbDrone.Core/Parser/Parser.cs b/NzbDrone.Core/Parser/Parser.cs index 2f88fe81e..3178b5084 100644 --- a/NzbDrone.Core/Parser/Parser.cs +++ b/NzbDrone.Core/Parser/Parser.cs @@ -74,7 +74,8 @@ namespace NzbDrone.Core.Parser private static readonly Regex MultiPartCleanupRegex = new Regex(@"\(\d+\)$", RegexOptions.Compiled); - private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?ita|italian)|(?german\b)|(?flemish)|(?greek)(?:\W|_)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?ita|italian)|(?german\b)|(?flemish)|(?greek)|(?(?:\W|_)FR)(?:\W|_)", + RegexOptions.IgnoreCase | RegexOptions.Compiled); public static ParsedEpisodeInfo ParsePath(string path) { @@ -119,7 +120,7 @@ namespace NzbDrone.Core.Parser break; result.Language = ParseLanguage(title); - result.Quality = ParseQuality(title); + result.Quality = QualityParser.ParseQuality(title); return result; } } @@ -240,155 +241,6 @@ namespace NzbDrone.Core.Parser return parseResult.SeriesTitle; } - private static QualityModel ParseQuality(string name) - { - Logger.Trace("Trying to parse quality for {0}", name); - - name = name.Trim(); - var normalizedName = CleanSeriesTitle(name); - var result = new QualityModel { Quality = Quality.Unknown }; - result.Proper = (normalizedName.Contains("proper") || normalizedName.Contains("repack")); - - if ((normalizedName.Contains("dvd") && !normalizedName.Contains("avcdvd")) || normalizedName.Contains("bdrip") || normalizedName.Contains("brrip")) - { - result.Quality = Quality.DVD; - return result; - } - - if (normalizedName.Contains("xvid") || normalizedName.Contains("divx") || normalizedName.Contains("dsr")) - { - if (normalizedName.Contains("bluray")) - { - result.Quality = Quality.DVD; - return result; - } - - result.Quality = Quality.SDTV; - return result; - } - - if (normalizedName.Contains("bluray")) - { - if (normalizedName.Contains("720p")) - { - result.Quality = Quality.Bluray720p; - return result; - } - - if (normalizedName.Contains("1080p")) - { - result.Quality = Quality.Bluray1080p; - return result; - } - - result.Quality = Quality.Bluray720p; - return result; - } - if (normalizedName.Contains("webdl") || normalizedName.Contains("webrip")) - { - if (normalizedName.Contains("1080p")) - { - result.Quality = Quality.WEBDL1080p; - return result; - } - - if (normalizedName.Contains("720p")) - { - result.Quality = Quality.WEBDL720p; - return result; - } - - if (name.Contains("[WEBDL]")) - { - result.Quality = Quality.WEBDL720p; - return result; - } - - result.Quality = Quality.WEBDL480p; - return result; - } - - if (normalizedName.Contains("trollhd") || normalizedName.Contains("rawhd")) - { - result.Quality = Quality.RAWHD; - return result; - } - - if (normalizedName.Contains("x264") || normalizedName.Contains("h264") || normalizedName.Contains("720p")) - { - if (normalizedName.Contains("1080p")) - { - result.Quality = Quality.HDTV1080p; - return result; - } - - result.Quality = Quality.HDTV720p; - return result; - } - - //Based on extension - if (result.Quality == Quality.Unknown && !name.ContainsInvalidPathChars()) - { - try - { - switch (Path.GetExtension(name).ToLower()) - { - case ".avi": - case ".xvid": - case ".divx": - case ".wmv": - case ".mp4": - case ".mpg": - case ".mpeg": - case ".mov": - case ".rm": - case ".rmvb": - case ".flv": - case ".dvr-ms": - case ".ogm": - case ".strm": - { - result.Quality = Quality.SDTV; - break; - } - case ".mkv": - case ".ts": - { - result.Quality = Quality.HDTV720p; - break; - } - } - } - catch (ArgumentException) - { - //Swallow exception for cases where string contains illegal - //path characters. - } - } - - if (name.Contains("[HDTV]")) - { - result.Quality = Quality.HDTV720p; - return result; - } - - if (normalizedName.Contains("hdtv") && normalizedName.Contains("1080p")) - { - result.Quality = Quality.HDTV1080p; - return result; - } - - if ((normalizedName.Contains("sdtv") || normalizedName.Contains("pdtv") || - (result.Quality == Quality.Unknown && normalizedName.Contains("hdtv"))) && - !normalizedName.Contains("mpeg")) - { - result.Quality = Quality.SDTV; - return result; - } - - return result; - } - private static Language ParseLanguage(string title) { var lowerTitle = title.ToLower(); @@ -461,6 +313,9 @@ namespace NzbDrone.Core.Parser if (match.Groups["greek"].Captures.Cast().Any()) return Language.Greek; + if (match.Groups["french"].Success) + return Language.French; + return Language.English; } diff --git a/NzbDrone.Core/Parser/QualityParser.cs b/NzbDrone.Core/Parser/QualityParser.cs new file mode 100644 index 000000000..8cd3077e2 --- /dev/null +++ b/NzbDrone.Core/Parser/QualityParser.cs @@ -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)| + (?WEB-DL|WEBDL|WEB\sDL|WEB\-DL|WebRip)| + (?HDTV)| + (?BDRiP)|(?BRRip)|(?\bDVD\b|DVDRip|NTSC|PAL|xvidvd)| + (?WS\sDSR|WS_DSR|WS\.DSR|DSR)|(?PDTV)|(?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)|(?h264)|(?XvidHD)|(?Xvid)|(?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 + } +} diff --git a/NzbDrone.Core/Providers/XemProvider.cs b/NzbDrone.Core/Providers/XemProvider.cs index 0881f4edc..931793dfb 100644 --- a/NzbDrone.Core/Providers/XemProvider.cs +++ b/NzbDrone.Core/Providers/XemProvider.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Common.Cache; using NzbDrone.Common.Messaging; +using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Tv; using NzbDrone.Core.Tv.Events; @@ -12,31 +14,37 @@ namespace NzbDrone.Core.Providers { void UpdateMappings(); void UpdateMappings(int seriesId); + void UpdateMappings(Series series); void PerformUpdate(Series series); } - public class XemProvider : IXemProvider, IExecute, IHandle + public class XemProvider : IXemProvider, IExecute, IHandle, IHandleAsync { private readonly IEpisodeService _episodeService; private readonly IXemCommunicationProvider _xemCommunicationProvider; private readonly ISeriesService _seriesService; + private readonly ICached _cache; private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); - public XemProvider(IEpisodeService episodeService, IXemCommunicationProvider xemCommunicationProvider, ISeriesService seriesService) + public XemProvider(IEpisodeService episodeService, + IXemCommunicationProvider xemCommunicationProvider, + ISeriesService seriesService, ICacheManger cacheManger) { if (seriesService == null) throw new ArgumentNullException("seriesService"); _episodeService = episodeService; _xemCommunicationProvider = xemCommunicationProvider; _seriesService = seriesService; + _cache = cacheManger.GetCache(GetType()); } public void UpdateMappings() { _logger.Trace("Starting scene numbering update"); + try { - var ids = _xemCommunicationProvider.GetXemSeriesIds(); + var ids = GetXemSeriesIds(); var series = _seriesService.GetAllSeries(); var wantedSeries = series.Where(s => ids.Contains(s.TvdbId)).ToList(); @@ -64,12 +72,15 @@ namespace NzbDrone.Core.Providers _logger.Trace("Series could not be found: {0}", seriesId); return; } + + UpdateMappings(series); + } - var xemIds = _xemCommunicationProvider.GetXemSeriesIds(); - - if (!xemIds.Contains(series.TvdbId)) + public void UpdateMappings(Series series) + { + if (!_cache.Find(series.TvdbId.ToString())) { - _logger.Trace("Xem doesn't have a mapping for this series: {0}", series.TvdbId); + _logger.Trace("Scene numbering is not available for {0} [{1}]", series.Title, series.TvdbId); return; } @@ -125,6 +136,20 @@ namespace NzbDrone.Core.Providers } } + private List GetXemSeriesIds() + { + _cache.Clear(); + + var ids = _xemCommunicationProvider.GetXemSeriesIds(); + + foreach (var id in ids) + { + _cache.Set(id.ToString(), true); + } + + return ids; + } + public void Execute(UpdateXemMappingsCommand message) { if (message.SeriesId.HasValue) @@ -139,7 +164,12 @@ namespace NzbDrone.Core.Providers public void Handle(SeriesUpdatedEvent message) { - PerformUpdate(message.Series); + UpdateMappings(message.Series); + } + + public void HandleAsync(ApplicationStartedEvent message) + { + GetXemSeriesIds(); } } } diff --git a/NzbDrone.Core/RootFolders/RootFolder.cs b/NzbDrone.Core/RootFolders/RootFolder.cs index e19725193..823265323 100644 --- a/NzbDrone.Core/RootFolders/RootFolder.cs +++ b/NzbDrone.Core/RootFolders/RootFolder.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.RootFolders { public string Path { get; set; } - public long FreeSpace { get; set; } + public long? FreeSpace { get; set; } public List UnmappedFolders { get; set; } } diff --git a/NzbDrone.Core/RootFolders/RootFolderService.cs b/NzbDrone.Core/RootFolders/RootFolderService.cs index 00e1a9cf0..160cd3c9f 100644 --- a/NzbDrone.Core/RootFolders/RootFolderService.cs +++ b/NzbDrone.Core/RootFolders/RootFolderService.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Core.RootFolders RootFolder Add(RootFolder rootDir); void Remove(int id); List GetUnmappedFolders(string path); - Dictionary FreeSpaceOnDrives(); + Dictionary FreeSpaceOnDrives(); RootFolder Get(int id); } @@ -55,7 +55,7 @@ namespace NzbDrone.Core.RootFolders { if (_diskProvider.FolderExists(folder.Path)) { - folder.FreeSpace = _diskProvider.GetAvilableSpace(folder.Path); + folder.FreeSpace = _diskProvider.GetAvailableSpace(folder.Path); folder.UnmappedFolders = GetUnmappedFolders(folder.Path); } }); @@ -82,7 +82,7 @@ namespace NzbDrone.Core.RootFolders _rootFolderRepository.Insert(rootFolder); - rootFolder.FreeSpace = _diskProvider.GetAvilableSpace(rootFolder.Path); + rootFolder.FreeSpace = _diskProvider.GetAvailableSpace(rootFolder.Path); rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path); return rootFolder; } @@ -126,9 +126,9 @@ namespace NzbDrone.Core.RootFolders return results; } - public Dictionary FreeSpaceOnDrives() + public Dictionary FreeSpaceOnDrives() { - var freeSpace = new Dictionary(); + var freeSpace = new Dictionary(); var rootDirs = All(); @@ -140,7 +140,7 @@ namespace NzbDrone.Core.RootFolders { try { - freeSpace.Add(pathRoot, _diskProvider.GetAvilableSpace(rootDir.Path)); + freeSpace.Add(pathRoot, _diskProvider.GetAvailableSpace(rootDir.Path)); } catch (Exception ex) { @@ -155,7 +155,7 @@ namespace NzbDrone.Core.RootFolders public RootFolder Get(int id) { var rootFolder = _rootFolderRepository.Get(id); - rootFolder.FreeSpace = _diskProvider.GetAvilableSpace(rootFolder.Path); + rootFolder.FreeSpace = _diskProvider.GetAvailableSpace(rootFolder.Path); rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path); return rootFolder; } diff --git a/NzbDrone.Core/Tv/Events/EpisodeInfoDeletedEvent.cs b/NzbDrone.Core/Tv/Events/EpisodeInfoDeletedEvent.cs new file mode 100644 index 000000000..864445e58 --- /dev/null +++ b/NzbDrone.Core/Tv/Events/EpisodeInfoDeletedEvent.cs @@ -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 Episodes { get; private set; } + + public EpisodeInfoDeletedEvent(IList episodes) + { + Episodes = new ReadOnlyCollection(episodes); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Tv/RefreshEpisodeService.cs b/NzbDrone.Core/Tv/RefreshEpisodeService.cs index 14140ba84..c62d8a5d2 100644 --- a/NzbDrone.Core/Tv/RefreshEpisodeService.cs +++ b/NzbDrone.Core/Tv/RefreshEpisodeService.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Tv var successCount = 0; var failCount = 0; - var existinEpisodes = _episodeService.GetEpisodeBySeries(series.Id); + var existingEpisodes = _episodeService.GetEpisodeBySeries(series.Id); var seasons = _seasonService.GetSeasonsBySeries(series.Id); var updateList = new List(); @@ -44,11 +44,11 @@ namespace NzbDrone.Core.Tv { try { - var episodeToUpdate = existinEpisodes.SingleOrDefault(e => e.SeasonNumber == episode.SeasonNumber && e.EpisodeNumber == episode.EpisodeNumber); + var episodeToUpdate = existingEpisodes.SingleOrDefault(e => e.SeasonNumber == episode.SeasonNumber && e.EpisodeNumber == episode.EpisodeNumber); if (episodeToUpdate != null) { - existinEpisodes.Remove(episodeToUpdate); + existingEpisodes.Remove(episodeToUpdate); updateList.Add(episodeToUpdate); } else @@ -82,11 +82,10 @@ namespace NzbDrone.Core.Tv AdjustMultiEpisodeAirTime(series, allEpisodes); - _episodeService.DeleteMany(existinEpisodes); + _episodeService.DeleteMany(existingEpisodes); _episodeService.UpdateMany(updateList); _episodeService.InsertMany(newList); - if (newList.Any()) { _messageAggregator.PublishEvent(new EpisodeInfoAddedEvent(newList, series)); @@ -97,6 +96,11 @@ namespace NzbDrone.Core.Tv _messageAggregator.PublishEvent(new EpisodeInfoUpdatedEvent(updateList)); } + if (existingEpisodes.Any()) + { + _messageAggregator.PublishEvent(new EpisodeInfoDeletedEvent(updateList)); + } + if (failCount != 0) { _logger.Info("Finished episode refresh for series: {0}. Successful: {1} - Failed: {2} ", diff --git a/NzbDrone.Core/Tv/SeasonService.cs b/NzbDrone.Core/Tv/SeasonService.cs index 8cb3d0c75..fddfc9f0f 100644 Binary files a/NzbDrone.Core/Tv/SeasonService.cs and b/NzbDrone.Core/Tv/SeasonService.cs differ diff --git a/NzbDrone.Core/Update/UpdateCheckService.cs b/NzbDrone.Core/Update/UpdateCheckService.cs index 36d43ea44..313f1ef58 100644 --- a/NzbDrone.Core/Update/UpdateCheckService.cs +++ b/NzbDrone.Core/Update/UpdateCheckService.cs @@ -26,7 +26,7 @@ namespace NzbDrone.Core.Update { var latestAvailable = _updatePackageProvider.GetLatestUpdate(); - if (latestAvailable == null || latestAvailable.Version <= BuildInfo.Version) + if (latestAvailable == null) { _logger.Debug("No update available."); return null; diff --git a/NzbDrone.Core/Update/UpdatePackage.cs b/NzbDrone.Core/Update/UpdatePackage.cs index e600e2a83..ce038ac99 100644 --- a/NzbDrone.Core/Update/UpdatePackage.cs +++ b/NzbDrone.Core/Update/UpdatePackage.cs @@ -1,11 +1,18 @@ using System; +using Newtonsoft.Json; namespace NzbDrone.Core.Update { public class UpdatePackage { - public string Url { get; set; } - public string FileName { get; set; } + public String Id { get; set; } + + [JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))] public Version Version { get; set; } + + public String Branch { get; set; } + public DateTime ReleaseDate { get; set; } + public String FileName { get; set; } + public String Url { get; set; } } } diff --git a/NzbDrone.Core/Update/UpdatePackageAvailable.cs b/NzbDrone.Core/Update/UpdatePackageAvailable.cs new file mode 100644 index 000000000..1d76e22d7 --- /dev/null +++ b/NzbDrone.Core/Update/UpdatePackageAvailable.cs @@ -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; } + } +} diff --git a/NzbDrone.Core/Update/UpdatePackageProvider.cs b/NzbDrone.Core/Update/UpdatePackageProvider.cs index f85dc6e66..e045a89fd 100644 --- a/NzbDrone.Core/Update/UpdatePackageProvider.cs +++ b/NzbDrone.Core/Update/UpdatePackageProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using Newtonsoft.Json; using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; @@ -11,56 +12,30 @@ namespace NzbDrone.Core.Update { public interface IUpdatePackageProvider { - IEnumerable GetAvailablePackages(); UpdatePackage GetLatestUpdate(); } public class UpdatePackageProvider : IUpdatePackageProvider { - private readonly IConfigFileProvider _configService; + private readonly IConfigFileProvider _configFileProvider; private readonly IHttpProvider _httpProvider; private readonly Logger _logger; - private static readonly Regex ParseRegex = new Regex(@"(?:\>)(?NzbDrone.+?(?(?<=\.)\d+\.\d+\.\d+\.\d+).+?)(?:\<\/a\>)", - RegexOptions.IgnoreCase); - - public UpdatePackageProvider(IConfigFileProvider configService, IHttpProvider httpProvider, Logger logger) + public UpdatePackageProvider(IConfigFileProvider configFileProvider, IHttpProvider httpProvider, Logger logger) { - _configService = configService; + _configFileProvider = configFileProvider; _httpProvider = httpProvider; _logger = logger; } - public IEnumerable GetAvailablePackages() - { - var updateList = new List(); - - 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) - { - 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 updateList; - } - public UpdatePackage GetLatestUpdate() { - return GetAvailablePackages().OrderByDescending(c => c.Version).FirstOrDefault(); + var url = String.Format("{0}/v1/update/{1}?version={2}", Services.RootUrl, _configFileProvider.Branch, BuildInfo.Version); + var update = JsonConvert.DeserializeObject(_httpProvider.DownloadString(url)); + + if (!update.Available) return null; + + return update.UpdatePackage; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Validation/RuleBuilderExtensions.cs b/NzbDrone.Core/Validation/RuleBuilderExtensions.cs index 6401fb475..da8609e84 100644 --- a/NzbDrone.Core/Validation/RuleBuilderExtensions.cs +++ b/NzbDrone.Core/Validation/RuleBuilderExtensions.cs @@ -23,6 +23,7 @@ namespace NzbDrone.Core.Validation public static IRuleBuilderOptions ValidRootUrl(this IRuleBuilder ruleBuilder) { + ruleBuilder.SetValidator(new NotEmptyValidator(null)); return ruleBuilder.SetValidator(new RegularExpressionValidator("^http(?:s)?://[a-z0-9-.]+", RegexOptions.IgnoreCase)).WithMessage("must be valid URL that"); } } diff --git a/NzbDrone.Host/AccessControl/FirewallAdapter.cs b/NzbDrone.Host/AccessControl/FirewallAdapter.cs index 96fb87679..72d194446 100644 --- a/NzbDrone.Host/AccessControl/FirewallAdapter.cs +++ b/NzbDrone.Host/AccessControl/FirewallAdapter.cs @@ -1,6 +1,7 @@ using System; using NetFwTypeLib; using NLog; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Configuration; namespace NzbDrone.Host.AccessControl @@ -31,9 +32,6 @@ namespace NzbDrone.Host.AccessControl return; } - CloseFirewallPort(); - - //Open the new port OpenFirewallPort(_configFileProvider.Port); } } @@ -91,38 +89,10 @@ namespace NzbDrone.Host.AccessControl } } - private void CloseFirewallPort() - { - try - { - var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); - var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType); - var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; - - var portNumber = 8989; - - foreach (INetFwOpenPort p in ports) - { - if (p.Name == "NzbDrone") - { - portNumber = p.Port; - break; - } - } - - if (portNumber != _configFileProvider.Port) - { - ports.Remove(portNumber, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP); - } - } - catch (Exception ex) - { - _logger.WarnException("Failed to close port in firewall for NzbDrone", ex); - } - } - private bool IsFirewallEnabled() { + if (OsInfo.IsLinux) return false; + try { var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); @@ -135,7 +105,5 @@ namespace NzbDrone.Host.AccessControl return false; } } - - } } diff --git a/NzbDrone.Host/AccessControl/UrlAclAdapter.cs b/NzbDrone.Host/AccessControl/UrlAclAdapter.cs index c7e1480d6..e536fffbd 100644 --- a/NzbDrone.Host/AccessControl/UrlAclAdapter.cs +++ b/NzbDrone.Host/AccessControl/UrlAclAdapter.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Host.AccessControl public interface IUrlAclAdapter { void RefreshRegistration(); + string UrlAcl { get; } } public class UrlAclAdapter : IUrlAclAdapter @@ -24,18 +25,25 @@ namespace NzbDrone.Host.AccessControl _logger = logger; } + public string UrlAcl + { + get + { + return "http://*:" + _configFileProvider.Port + "/"; + } + } + public void RefreshRegistration() { if (OsInfo.Version.Major < 6) return; - RegisterUrl(_configFileProvider.Port); + RegisterUrl(); } - - private void RegisterUrl(int portNumber) + private void RegisterUrl() { - var arguments = String.Format("http add urlacl http://*:{0}/ sddl=D:(A;;GX;;;S-1-1-0)", portNumber); + var arguments = String.Format("http add urlacl {0} sddl=D:(A;;GX;;;S-1-1-0)", UrlAcl); RunNetsh(arguments); } diff --git a/NzbDrone.Host/ApplicationServer.cs b/NzbDrone.Host/ApplicationServer.cs index b1abc00c5..d6e1e3625 100644 --- a/NzbDrone.Host/ApplicationServer.cs +++ b/NzbDrone.Host/ApplicationServer.cs @@ -4,7 +4,6 @@ using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Configuration; -using NzbDrone.Host.AccessControl; using NzbDrone.Host.Owin; namespace NzbDrone.Host @@ -23,13 +22,10 @@ namespace NzbDrone.Host private readonly IProcessProvider _processProvider; private readonly PriorityMonitor _priorityMonitor; private readonly IStartupArguments _startupArguments; - private readonly IFirewallAdapter _firewallAdapter; - private readonly IUrlAclAdapter _urlAclAdapter; private readonly Logger _logger; public NzbDroneServiceFactory(IConfigFileProvider configFileProvider, IHostController hostController, IRuntimeInfo runtimeInfo, - IProcessProvider processProvider, PriorityMonitor priorityMonitor, IStartupArguments startupArguments, - IFirewallAdapter firewallAdapter, IUrlAclAdapter urlAclAdapter, Logger logger) + IProcessProvider processProvider, PriorityMonitor priorityMonitor, IStartupArguments startupArguments, Logger logger) { _configFileProvider = configFileProvider; _hostController = hostController; @@ -37,8 +33,6 @@ namespace NzbDrone.Host _processProvider = processProvider; _priorityMonitor = priorityMonitor; _startupArguments = startupArguments; - _firewallAdapter = firewallAdapter; - _urlAclAdapter = urlAclAdapter; _logger = logger; } @@ -49,11 +43,6 @@ namespace NzbDrone.Host public void Start() { - if (OsInfo.IsWindows && _runtimeInfo.IsAdmin) - { - _urlAclAdapter.RefreshRegistration(); - _firewallAdapter.MakeAccessible(); - } _hostController.StartServer(); if (!_startupArguments.Flags.Contains(StartupArguments.NO_BROWSER) && diff --git a/NzbDrone.Host/MainAppContainerBuilder.cs b/NzbDrone.Host/MainAppContainerBuilder.cs index 51f47d415..e12fc9153 100644 --- a/NzbDrone.Host/MainAppContainerBuilder.cs +++ b/NzbDrone.Host/MainAppContainerBuilder.cs @@ -1,4 +1,5 @@ -using Nancy.Bootstrapper; +using System; +using Nancy.Bootstrapper; using NzbDrone.Api; using NzbDrone.Api.SignalR; using NzbDrone.Common.Composition; @@ -6,6 +7,7 @@ using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Datastore; using NzbDrone.Core.Organizer; using NzbDrone.Core.RootFolders; +using NzbDrone.Host.Owin; namespace NzbDrone.Host { diff --git a/NzbDrone.Host/NzbDrone.Host.csproj b/NzbDrone.Host/NzbDrone.Host.csproj index 2eb6bcda7..c625626ea 100644 --- a/NzbDrone.Host/NzbDrone.Host.csproj +++ b/NzbDrone.Host/NzbDrone.Host.csproj @@ -122,6 +122,9 @@ + + + diff --git a/NzbDrone.Host/Owin/IHostController.cs b/NzbDrone.Host/Owin/IHostController.cs index 9df77feb4..026bff89e 100644 --- a/NzbDrone.Host/Owin/IHostController.cs +++ b/NzbDrone.Host/Owin/IHostController.cs @@ -4,7 +4,6 @@ { string AppUrl { get; } void StartServer(); - void RestartServer(); void StopServer(); } } \ No newline at end of file diff --git a/NzbDrone.Host/Owin/NlogTextWriter.cs b/NzbDrone.Host/Owin/NlogTextWriter.cs new file mode 100644 index 000000000..d4cd4da7d --- /dev/null +++ b/NzbDrone.Host/Owin/NlogTextWriter.cs @@ -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); + } + + } +} \ No newline at end of file diff --git a/NzbDrone.Host/Owin/OwinHostController.cs b/NzbDrone.Host/Owin/OwinHostController.cs index 0055883b6..9968b22bb 100644 --- a/NzbDrone.Host/Owin/OwinHostController.cs +++ b/NzbDrone.Host/Owin/OwinHostController.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Owin.Hosting; using NLog; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Security; using NzbDrone.Core.Configuration; +using NzbDrone.Host.AccessControl; using NzbDrone.Host.Owin.MiddleWare; using Owin; @@ -14,13 +16,20 @@ namespace NzbDrone.Host.Owin { private readonly IConfigFileProvider _configFileProvider; private readonly IEnumerable _owinMiddleWares; + private readonly IRuntimeInfo _runtimeInfo; + private readonly IUrlAclAdapter _urlAclAdapter; + private readonly IFirewallAdapter _firewallAdapter; private readonly Logger _logger; private IDisposable _host; - public OwinHostController(IConfigFileProvider configFileProvider, IEnumerable owinMiddleWares, Logger logger) + public OwinHostController(IConfigFileProvider configFileProvider, IEnumerable owinMiddleWares, + IRuntimeInfo runtimeInfo, IUrlAclAdapter urlAclAdapter, IFirewallAdapter firewallAdapter, Logger logger) { _configFileProvider = configFileProvider; _owinMiddleWares = owinMiddleWares; + _runtimeInfo = runtimeInfo; + _urlAclAdapter = urlAclAdapter; + _firewallAdapter = firewallAdapter; _logger = logger; } @@ -28,16 +37,20 @@ namespace NzbDrone.Host.Owin { IgnoreCertErrorPolicy.Register(); - var url = "http://*:" + _configFileProvider.Port; + if (OsInfo.IsWindows && _runtimeInfo.IsAdmin) + { + _urlAclAdapter.RefreshRegistration(); + _firewallAdapter.MakeAccessible(); + } - var options = new StartOptions(url) + var options = new StartOptions(_urlAclAdapter.UrlAcl) { ServerFactory = "Microsoft.Owin.Host.HttpListener" }; - _logger.Info("starting server on {0}", url); + _logger.Info("starting server on {0}", _urlAclAdapter.UrlAcl); - _host = WebApp.Start(options, BuildApp); + _host = WebApp.Start(OwinServiceProviderFactory.Create(), options, BuildApp); } private void BuildApp(IAppBuilder appBuilder) @@ -56,14 +69,6 @@ namespace NzbDrone.Host.Owin get { return string.Format("http://localhost:{0}", _configFileProvider.Port); } } - public void RestartServer() - { - _logger.Warn("Attempting to restart server."); - - StopServer(); - StartServer(); - } - public void StopServer() { if (_host == null) return; diff --git a/NzbDrone.Host/Owin/OwinServiceProvider.cs b/NzbDrone.Host/Owin/OwinServiceProvider.cs new file mode 100644 index 000000000..cffea738f --- /dev/null +++ b/NzbDrone.Host/Owin/OwinServiceProvider.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Host/Owin/OwinTraceOutputFactory.cs b/NzbDrone.Host/Owin/OwinTraceOutputFactory.cs new file mode 100644 index 000000000..9a0f8997d --- /dev/null +++ b/NzbDrone.Host/Owin/OwinTraceOutputFactory.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Integration.Test/EpisodeIntegrationTests.cs b/NzbDrone.Integration.Test/EpisodeIntegrationTests.cs index 411023cac..f4d7fe8c1 100644 --- a/NzbDrone.Integration.Test/EpisodeIntegrationTests.cs +++ b/NzbDrone.Integration.Test/EpisodeIntegrationTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using NUnit.Framework; using NzbDrone.Api.Series; using System.Linq; +using NzbDrone.Test.Common; namespace NzbDrone.Integration.Test { @@ -15,7 +16,7 @@ namespace NzbDrone.Integration.Test var series = Series.Lookup("archer").First(); series.QualityProfileId = 1; - series.Path = @"C:\Test\Archer"; + series.Path = @"C:\Test\Archer".AsOsAgnostic(); series = Series.Post(series); @@ -57,4 +58,4 @@ namespace NzbDrone.Integration.Test Episodes.Put(updatedEpisode).Monitored.Should().BeFalse(); } } -} \ No newline at end of file +} diff --git a/NzbDrone.Integration.Test/IntegrationTest.cs b/NzbDrone.Integration.Test/IntegrationTest.cs index 3ef126a98..b858e4ff3 100644 --- a/NzbDrone.Integration.Test/IntegrationTest.cs +++ b/NzbDrone.Integration.Test/IntegrationTest.cs @@ -1,4 +1,5 @@ -using NLog; +using System.Runtime.CompilerServices; +using NLog; using NLog.Config; using NLog.Targets; using NUnit.Framework; @@ -39,6 +40,7 @@ namespace NzbDrone.Integration.Test LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, consoleTarget)); } + //[TestFixtureSetUp] [SetUp] public void SmokeTestSetup() { @@ -63,6 +65,7 @@ namespace NzbDrone.Integration.Test NamingConfig = new ClientBase(RestClient, "config/naming"); } + //[TestFixtureTearDown] [TearDown] public void SmokeTestTearDown() { diff --git a/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj b/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj index 2ffdbc7a9..5762f6877 100644 --- a/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj +++ b/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj @@ -33,8 +33,9 @@ MinimumRecommendedRules.ruleset - - ..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll + + False + ..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll False diff --git a/NzbDrone.Integration.Test/NzbDroneRunner.cs b/NzbDrone.Integration.Test/NzbDroneRunner.cs index 988c47278..5955166ff 100644 --- a/NzbDrone.Integration.Test/NzbDroneRunner.cs +++ b/NzbDrone.Integration.Test/NzbDroneRunner.cs @@ -53,11 +53,17 @@ namespace NzbDrone.Integration.Test Assert.Fail("Process has exited"); } - if (_restClient.Get(new RestRequest("system/status")).ResponseStatus == ResponseStatus.Completed) + + var statusCall = _restClient.Get(new RestRequest("system/status")); + + if (statusCall.ResponseStatus == ResponseStatus.Completed) { + Console.WriteLine("NzbDrone is started. Running Tests"); return; } + Console.WriteLine("Waiting for NzbDrone to start. Response Status : {0} [{1}] {2}", statusCall.ResponseStatus, statusCall.StatusDescription, statusCall.ErrorException); + Thread.Sleep(500); } } diff --git a/NzbDrone.Integration.Test/RootFolderIntegrationTest.cs b/NzbDrone.Integration.Test/RootFolderIntegrationTest.cs index cd1b29cf0..8a888fffa 100644 --- a/NzbDrone.Integration.Test/RootFolderIntegrationTest.cs +++ b/NzbDrone.Integration.Test/RootFolderIntegrationTest.cs @@ -58,5 +58,17 @@ namespace NzbDrone.Integration.Test RootFolders.All().Should().BeEmpty(); } + + [Test] + public void invalid_path_should_return_bad_request() + { + var rootFolder = new RootFolderResource + { + Path = "invalid_path" + }; + + var postResponse = RootFolders.InvalidPost(rootFolder); + postResponse.Should().NotBeEmpty(); + } } } \ No newline at end of file diff --git a/NzbDrone.Integration.Test/SeasonIntegrationTests.cs b/NzbDrone.Integration.Test/SeasonIntegrationTests.cs index c10146030..7a2458b4f 100644 --- a/NzbDrone.Integration.Test/SeasonIntegrationTests.cs +++ b/NzbDrone.Integration.Test/SeasonIntegrationTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using NUnit.Framework; using NzbDrone.Api.Series; using System.Linq; +using NzbDrone.Test.Common; namespace NzbDrone.Integration.Test { @@ -15,7 +16,7 @@ namespace NzbDrone.Integration.Test var series = Series.Lookup("archer").First(); series.QualityProfileId = 1; - series.Path = @"C:\Test\Archer"; + series.Path = @"C:\Test\Archer".AsOsAgnostic(); series = Series.Post(series); @@ -57,4 +58,4 @@ namespace NzbDrone.Integration.Test Seasons.Put(updatedSeason).Monitored.Should().BeFalse(); } } -} \ No newline at end of file +} diff --git a/NzbDrone.Integration.Test/SeriesIntegrationTest.cs b/NzbDrone.Integration.Test/SeriesIntegrationTest.cs index 85ef89050..df6dce79d 100644 --- a/NzbDrone.Integration.Test/SeriesIntegrationTest.cs +++ b/NzbDrone.Integration.Test/SeriesIntegrationTest.cs @@ -3,18 +3,13 @@ using FluentAssertions; using NUnit.Framework; using NzbDrone.Api.Series; using System.Linq; +using NzbDrone.Test.Common; namespace NzbDrone.Integration.Test { [TestFixture] public class SeriesIntegrationTest : IntegrationTest { - [Test] - public void should_have_no_series_on_start_application() - { - Series.All().Should().BeEmpty(); - } - [Test] public void series_lookup_on_trakt() { @@ -37,7 +32,7 @@ namespace NzbDrone.Integration.Test var series = Series.Lookup("archer").First(); series.QualityProfileId = 1; - series.Path = @"C:\Test\Archer"; + series.Path = @"C:\Test\Archer".AsOsAgnostic(); series = Series.Post(series); @@ -57,7 +52,7 @@ namespace NzbDrone.Integration.Test var series = Series.Lookup("90210").First(); series.QualityProfileId = 1; - series.Path = @"C:\Test\90210"; + series.Path = @"C:\Test\90210".AsOsAgnostic(); series = Series.Post(series); diff --git a/NzbDrone.Integration.Test/packages.config b/NzbDrone.Integration.Test/packages.config index f0ee04f81..268fde156 100644 --- a/NzbDrone.Integration.Test/packages.config +++ b/NzbDrone.Integration.Test/packages.config @@ -1,6 +1,6 @@  - + diff --git a/NzbDrone.Test.Common/NzbDrone.Test.Common.csproj b/NzbDrone.Test.Common/NzbDrone.Test.Common.csproj index b3bb03f0c..4e85ec162 100644 --- a/NzbDrone.Test.Common/NzbDrone.Test.Common.csproj +++ b/NzbDrone.Test.Common/NzbDrone.Test.Common.csproj @@ -34,8 +34,9 @@ MinimumRecommendedRules.ruleset - - ..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll + + False + ..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll ..\packages\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll diff --git a/NzbDrone.Test.Common/packages.config b/NzbDrone.Test.Common/packages.config index 8c1697cb3..6880e0bf8 100644 --- a/NzbDrone.Test.Common/packages.config +++ b/NzbDrone.Test.Common/packages.config @@ -1,7 +1,7 @@  - + diff --git a/NzbDrone.Update.Test/NzbDrone.Update.Test.csproj b/NzbDrone.Update.Test/NzbDrone.Update.Test.csproj index 2401ae6d2..3dbc57b35 100644 --- a/NzbDrone.Update.Test/NzbDrone.Update.Test.csproj +++ b/NzbDrone.Update.Test/NzbDrone.Update.Test.csproj @@ -43,9 +43,9 @@ ..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll - + False - ..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll + ..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll diff --git a/NzbDrone.Update.Test/packages.config b/NzbDrone.Update.Test/packages.config index 3e6b4e094..6f925b437 100644 --- a/NzbDrone.Update.Test/packages.config +++ b/NzbDrone.Update.Test/packages.config @@ -1,6 +1,6 @@  - + diff --git a/NzbDrone/NzbDrone.csproj b/NzbDrone/NzbDrone.csproj index b599ed47b..5d45930cd 100644 --- a/NzbDrone/NzbDrone.csproj +++ b/NzbDrone/NzbDrone.csproj @@ -83,6 +83,10 @@ False ..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll + + False + ..\packages\NLog.2.0.1.2\lib\net40\NLog.dll + False ..\packages\Owin.1.0\lib\net40\Owin.dll diff --git a/NzbDrone/WindowsApp.cs b/NzbDrone/WindowsApp.cs index 0ded14d61..4d4dc9d69 100644 --- a/NzbDrone/WindowsApp.cs +++ b/NzbDrone/WindowsApp.cs @@ -1,5 +1,6 @@ using System; using System.Windows.Forms; +using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Host; using NzbDrone.SysTray; @@ -8,6 +9,7 @@ namespace NzbDrone { public static class WindowsApp { + public static void Main(string[] args) { try diff --git a/NzbDrone/packages.config b/NzbDrone/packages.config index 907d39c5a..19f3a3d8b 100644 --- a/NzbDrone/packages.config +++ b/NzbDrone/packages.config @@ -5,5 +5,6 @@ + \ No newline at end of file diff --git a/UI/AddSeries/RootFolders/Collection.js b/UI/AddSeries/RootFolders/Collection.js index 8f20ac92a..74b9b4555 100644 --- a/UI/AddSeries/RootFolders/Collection.js +++ b/UI/AddSeries/RootFolders/Collection.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict'; define( [ 'backbone', diff --git a/UI/AddSeries/RootFolders/Layout.js b/UI/AddSeries/RootFolders/Layout.js index 89388b2c2..3a75c0f9f 100644 --- a/UI/AddSeries/RootFolders/Layout.js +++ b/UI/AddSeries/RootFolders/Layout.js @@ -7,10 +7,11 @@ define( 'AddSeries/RootFolders/Collection', 'AddSeries/RootFolders/Model', 'Shared/LoadingView', + 'Mixins/AsValidatedView', 'Mixins/AutoComplete' - ], function (Marionette, RootFolderCollectionView, RootFolderCollection, RootFolderModel, LoadingView) { + ], function (Marionette, RootFolderCollectionView, RootFolderCollection, RootFolderModel, LoadingView, AsValidatedView) { - return Marionette.Layout.extend({ + var layout = Marionette.Layout.extend({ template: 'AddSeries/RootFolders/LayoutTemplate', ui: { @@ -55,12 +56,16 @@ define( Path: this.ui.pathInput.val() }); - RootFolderCollection.add(newDir); + this.bindToModelValidation(newDir); newDir.save().done(function () { + RootFolderCollection.add(newDir); self.trigger('folderSelected', {model: newDir}); }); } }); + + return AsValidatedView.apply(layout); + }); diff --git a/UI/AddSeries/RootFolders/LayoutTemplate.html b/UI/AddSeries/RootFolders/LayoutTemplate.html index 2a5d5f350..a62ef128d 100644 --- a/UI/AddSeries/RootFolders/LayoutTemplate.html +++ b/UI/AddSeries/RootFolders/LayoutTemplate.html @@ -3,10 +3,13 @@

Select Folder

@@ -79,7 +79,7 @@ - + diff --git a/UI/Settings/DownloadClient/PneumaticViewTemplate.html b/UI/Settings/DownloadClient/PneumaticViewTemplate.html index 791b212ba..8fc612fc3 100644 --- a/UI/Settings/DownloadClient/PneumaticViewTemplate.html +++ b/UI/Settings/DownloadClient/PneumaticViewTemplate.html @@ -6,7 +6,7 @@
- +
diff --git a/UI/Settings/DownloadClient/SabViewTemplate.html b/UI/Settings/DownloadClient/SabViewTemplate.html index 8bdd39afd..25ae6e74a 100644 --- a/UI/Settings/DownloadClient/SabViewTemplate.html +++ b/UI/Settings/DownloadClient/SabViewTemplate.html @@ -70,11 +70,11 @@ - + - + @@ -92,7 +92,7 @@ - + diff --git a/UI/Settings/Quality/Profile/EditQualityProfileTemplate.html b/UI/Settings/Quality/Profile/EditQualityProfileTemplate.html index 021a494d7..f45c97dda 100644 --- a/UI/Settings/Quality/Profile/EditQualityProfileTemplate.html +++ b/UI/Settings/Quality/Profile/EditQualityProfileTemplate.html @@ -12,9 +12,6 @@
- - -
diff --git a/UI/jQuery/jquery.validation.js b/UI/jQuery/jquery.validation.js index fda5087c5..ca03d0aa7 100644 --- a/UI/jQuery/jquery.validation.js +++ b/UI/jQuery/jquery.validation.js @@ -8,6 +8,10 @@ define( var validationName = error.propertyName.toLowerCase(); + this.find('.validation-errors') + .addClass('alert alert-error') + .append('
' + error.errorMessage + '
'); + var input = this.find('[name]').filter(function () { return this.name.toLowerCase() === validationName; }); @@ -40,11 +44,12 @@ define( }; $.fn.addFormError = function (error) { - this.find('.control-group').parent().prepend('
'+ error.errorMessage +'
') + this.find('.control-group').parent().prepend('
' + error.errorMessage + '
') }; $.fn.removeAllErrors = function () { this.find('.error').removeClass('error'); + this.find('.validation-errors').removeClass('alert').removeClass('alert-error').html(''); this.find('.validation-error').remove(); return this.find('.help-inline.error-message').remove(); }; diff --git a/debian/changelog b/debian/changelog index e15e9a293..eb8f841a6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ - nzbdrone (2.0.12-1) unstable; urgency=low -2 -3 * Initial release -4 -5 -- kay.one Mon, 22 Mar 2010 00:37:31 +0100 \ No newline at end of file +nzbdrone {version} {branch}; urgency=low + + * Automatic Release. + + -- NzbDrone Mon, 26 Aug 2013 00:00:00 -0700 diff --git a/debian/compat b/debian/compat new file mode 100644 index 000000000..45a4fb75d --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 diff --git a/debian/control b/debian/control index 174fecb48..e7f6a7b11 100644 --- a/debian/control +++ b/debian/control @@ -1,14 +1,12 @@ -Package: nzbdrone -Standards-Version: 2.0.0.226 -Architecture: all -Maintainer: Keivan Beigi -Depends: mono-runtime (>= 2.10.1) Section: web Priority: optional - +Maintainer: NzbDrone +Source: nzbdrone Homepage: http://www.nzbdrone.com Vcs-Git: git@github.com:NzbDrone/NzbDrone.git Vcs-Browser: https://github.com/NzbDrone/NzbDrone -Description: NZBDrone is a PVR for newsgroup users. -It can monitor multiple RSS feeds for new episodes of your favourite shows and will grab, sorts and renames them. It can also be configured to automatically upgrade the quality of files already downloaded if a better quality format becomes available. \ No newline at end of file +Package: nzbdrone +Architecture: all +Depends: libmono-cil-dev (>= 2.10.1) +Description: NZBDrone is a PVR for newsgroup users diff --git a/debian/copyright b/debian/copyright old mode 100644 new mode 100755 index 28e595dc8..cf281685c --- a/debian/copyright +++ b/debian/copyright @@ -1,25 +1,24 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: nzbdrone -Source: https://github.com/NzbDrone/NzbDrone - -Files: * -Copyright: 2010-2013 Keivan Beigi - 2010-2013 Mark McDowall - -License: GPL-3.0+ - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see . - . - On Debian systems, the complete text of the GNU General - Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". \ No newline at end of file +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: nzbdrone +Source: https://github.com/NzbDrone/NzbDrone + +Files: * +Copyright: 2010-2013 NzbDrone + +License: GPL-3.0+ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/files b/debian/files deleted file mode 100644 index 5957c8b62..000000000 --- a/debian/files +++ /dev/null @@ -1 +0,0 @@ -nzbdrone_226_i386.deb unknown extra diff --git a/debian/install b/debian/install old mode 100644 new mode 100755 index 46a453a03..106b06a9b --- a/debian/install +++ b/debian/install @@ -1,2 +1 @@ -nzbdrone_226/* opt/nzbdrone -../nzbdrone.desktop usr/share/applications +nzbdrone_bin/* opt/NzbDrone diff --git a/debian/rules b/debian/rules old mode 100644 new mode 100755 index fcc2b6104..b760bee7f --- a/debian/rules +++ b/debian/rules @@ -7,7 +7,7 @@ # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. -export DH_VERBOSE=1 +#export DH_VERBOSE=1 %: dh $@