Merge branch 'develop'

This commit is contained in:
Mark McDowall 2014-06-22 13:01:31 -07:00
commit dbb499c33c
296 changed files with 6078 additions and 2774 deletions

View File

@ -5,6 +5,7 @@ using NzbDrone.Common;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Reflection;
using NzbDrone.Core.Annotations;
using Omu.ValueInjecter;
namespace NzbDrone.Api.ClientSchema
{
@ -55,7 +56,7 @@ namespace NzbDrone.Api.ClientSchema
}
public static object ReadFormSchema(List<Field> fields, Type targetType)
public static object ReadFormSchema(List<Field> fields, Type targetType, object defaults = null)
{
Ensure.That(targetType, () => targetType).IsNotNull();
@ -63,6 +64,11 @@ namespace NzbDrone.Api.ClientSchema
var target = Activator.CreateInstance(targetType);
if (defaults != null)
{
target.InjectFrom(defaults);
}
foreach (var propertyInfo in properties)
{
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);

View File

@ -9,9 +9,12 @@ namespace NzbDrone.Api.Config
public String DownloadClientWorkingFolders { get; set; }
public Int32 DownloadedEpisodesScanInterval { get; set; }
public Boolean EnableCompletedDownloadHandling { get; set; }
public Boolean RemoveCompletedDownloads { get; set; }
public Boolean EnableFailedDownloadHandling { get; set; }
public Boolean AutoRedownloadFailed { get; set; }
public Boolean RemoveFailedDownloads { get; set; }
public Boolean EnableFailedDownloadHandling { get; set; }
public Int32 BlacklistGracePeriod { get; set; }
public Int32 BlacklistRetryInterval { get; set; }
public Int32 BlacklistRetryLimit { get; set; }

View File

@ -1,10 +1,11 @@
using System;
using NzbDrone.Core.Indexers;
namespace NzbDrone.Api.DownloadClient
{
public class DownloadClientResource : ProviderResource
{
public Boolean Enable { get; set; }
public Int32 Protocol { get; set; }
public DownloadProtocol Protocol { get; set; }
}
}

View File

@ -1,37 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Api.ClientSchema;
using NzbDrone.Core.Download;
using Omu.ValueInjecter;
namespace NzbDrone.Api.DownloadClient
{
public class DownloadClientSchemaModule : NzbDroneRestModule<DownloadClientResource>
{
private readonly IDownloadClientFactory _notificationFactory;
public DownloadClientSchemaModule(IDownloadClientFactory notificationFactory)
: base("downloadclient/schema")
{
_notificationFactory = notificationFactory;
GetResourceAll = GetSchema;
}
private List<DownloadClientResource> GetSchema()
{
var notifications = _notificationFactory.Templates();
var result = new List<DownloadClientResource>(notifications.Count);
foreach (var notification in notifications)
{
var notificationResource = new DownloadClientResource();
notificationResource.InjectFrom(notification);
notificationResource.Fields = SchemaBuilder.ToSchema(notification.Settings);
result.Add(notificationResource);
}
return result;
}
}
}

View File

@ -8,5 +8,6 @@ namespace NzbDrone.Api.Health
{
public HealthCheckResult Type { get; set; }
public String Message { get; set; }
public Uri WikiUrl { get; set; }
}
}

View File

@ -1,9 +1,11 @@
using System;
using NzbDrone.Core.Indexers;
namespace NzbDrone.Api.Indexers
{
public class IndexerResource : ProviderResource
{
public Boolean Enable { get; set; }
public DownloadProtocol Protocol { get; set; }
}
}

View File

@ -1,38 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Api.ClientSchema;
using NzbDrone.Core.Indexers;
using Omu.ValueInjecter;
namespace NzbDrone.Api.Indexers
{
public class IndexerSchemaModule : NzbDroneRestModule<IndexerResource>
{
private readonly IIndexerFactory _indexerFactory;
public IndexerSchemaModule(IIndexerFactory indexerFactory)
: base("indexer/schema")
{
_indexerFactory = indexerFactory;
GetResourceAll = GetSchema;
}
private List<IndexerResource> GetSchema()
{
var indexers = _indexerFactory.Templates().Where(c => c.Implementation =="Newznab");
var result = new List<IndexerResource>(indexers.Count());
foreach (var indexer in indexers)
{
var indexerResource = new IndexerResource();
indexerResource.InjectFrom(indexer);
indexerResource.Fields = SchemaBuilder.ToSchema(indexer.Settings);
result.Add(indexerResource);
}
return result;
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using NzbDrone.Api.REST;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Indexers;
namespace NzbDrone.Api.Indexers
{
@ -30,5 +31,6 @@ namespace NzbDrone.Api.Indexers
public String DownloadUrl { get; set; }
public String InfoUrl { get; set; }
public Boolean DownloadAllowed { get; set; }
public DownloadProtocol DownloadProtocol { get; set; }
}
}

View File

@ -1,37 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Api.ClientSchema;
using NzbDrone.Core.Notifications;
using Omu.ValueInjecter;
namespace NzbDrone.Api.Notifications
{
public class NotificationSchemaModule : NzbDroneRestModule<NotificationResource>
{
private readonly INotificationFactory _notificationFactory;
public NotificationSchemaModule(INotificationFactory notificationFactory)
: base("notification/schema")
{
_notificationFactory = notificationFactory;
GetResourceAll = GetSchema;
}
private List<NotificationResource> GetSchema()
{
var notifications = _notificationFactory.Templates();
var result = new List<NotificationResource>(notifications.Count);
foreach (var notification in notifications)
{
var notificationResource = new NotificationResource();
notificationResource.InjectFrom(notification);
notificationResource.Fields = SchemaBuilder.ToSchema(notification.Settings);
result.Add(notificationResource);
}
return result;
}
}
}

View File

@ -144,11 +144,9 @@
<Compile Include="MediaCovers\MediaCoverModule.cs" />
<Compile Include="Metadata\MetadataResource.cs" />
<Compile Include="Metadata\MetadataModule.cs" />
<Compile Include="Notifications\NotificationSchemaModule.cs" />
<Compile Include="NzbDroneFeedModule.cs" />
<Compile Include="ProviderResource.cs" />
<Compile Include="ProviderModuleBase.cs" />
<Compile Include="Indexers\IndexerSchemaModule.cs" />
<Compile Include="Indexers\IndexerModule.cs" />
<Compile Include="Indexers\IndexerResource.cs" />
<Compile Include="Indexers\ReleaseModule.cs" />
@ -172,7 +170,6 @@
<Compile Include="Queue\QueueModule.cs" />
<Compile Include="Queue\QueueResource.cs" />
<Compile Include="ResourceChangeMessage.cs" />
<Compile Include="DownloadClient\DownloadClientSchemaModule.cs" />
<Compile Include="Notifications\NotificationModule.cs" />
<Compile Include="Notifications\NotificationResource.cs" />
<Compile Include="NzbDroneRestModule.cs" />

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using Nancy;
@ -22,7 +23,7 @@ namespace NzbDrone.Api
: base(resource)
{
_providerFactory = providerFactory;
Get["templates"] = x => GetTemplates();
Get["schema"] = x => GetTemplates();
GetResourceAll = GetAll;
GetResourceById = GetProviderById;
CreateResource = CreateProvider;
@ -30,10 +31,11 @@ namespace NzbDrone.Api
DeleteResource = DeleteProvider;
SharedValidator.RuleFor(c => c.Name).NotEmpty();
SharedValidator.RuleFor(c => c.Name).Must((v,c) => !_providerFactory.All().Any(p => p.Name == c && p.Id != v.Id)).WithMessage("Should be unique");
SharedValidator.RuleFor(c => c.Implementation).NotEmpty();
SharedValidator.RuleFor(c => c.ConfigContract).NotEmpty();
PostValidator.RuleFor(c => c.Fields).NotEmpty();
PostValidator.RuleFor(c => c.Fields).NotNull();
}
private TProviderResource GetProviderById(int id)
@ -81,8 +83,13 @@ namespace NzbDrone.Api
definition.InjectFrom(providerResource);
var preset = _providerFactory.GetPresetDefinitions(definition)
.Where(v => v.Name == definition.Name)
.Select(v => v.Settings)
.FirstOrDefault();
var configContract = ReflectionExtensions.CoreAssembly.FindTypeByName(definition.ConfigContract);
definition.Settings = (IProviderConfig)SchemaBuilder.ReadFormSchema(providerResource.Fields, configContract);
definition.Settings = (IProviderConfig)SchemaBuilder.ReadFormSchema(providerResource.Fields, configContract, preset);
Validate(definition);
@ -96,15 +103,29 @@ namespace NzbDrone.Api
private Response GetTemplates()
{
var templates = _providerFactory.Templates();
var defaultDefinitions = _providerFactory.GetDefaultDefinitions();
var result = new List<TProviderResource>(templates.Count());
var result = new List<TProviderResource>(defaultDefinitions.Count());
foreach (var providerDefinition in templates)
foreach (var providerDefinition in defaultDefinitions)
{
var providerResource = new TProviderResource();
providerResource.InjectFrom(providerDefinition);
providerResource.Fields = SchemaBuilder.ToSchema(providerDefinition.Settings);
providerResource.InfoLink = String.Format("https://github.com/NzbDrone/NzbDrone/wiki/Supported-{0}#{1}",
typeof(TProviderResource).Name.Replace("Resource", "s"),
providerDefinition.Implementation.ToLower());
var presetDefinitions = _providerFactory.GetPresetDefinitions(providerDefinition);
providerResource.Presets = presetDefinitions.Select(v =>
{
var presetResource = new TProviderResource();
presetResource.InjectFrom(v);
presetResource.Fields = SchemaBuilder.ToSchema(v.Settings);
return presetResource as ProviderResource;
}).ToList();
result.Add(providerResource);
}

View File

@ -11,5 +11,8 @@ namespace NzbDrone.Api
public List<Field> Fields { get; set; }
public String Implementation { get; set; }
public String ConfigContract { get; set; }
public String InfoLink { get; set; }
public List<ProviderResource> Presets { get; set; }
}
}

View File

@ -2,18 +2,20 @@
using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Api.Series;
using NzbDrone.Api.Episodes;
namespace NzbDrone.Api.Queue
{
public class QueueResource : RestResource
{
public Core.Tv.Series Series { get; set; }
public Episode Episode { get; set; }
public SeriesResource Series { get; set; }
public EpisodeResource Episode { get; set; }
public QualityModel Quality { get; set; }
public Decimal Size { get; set; }
public String Title { get; set; }
public Decimal Sizeleft { get; set; }
public TimeSpan Timeleft { get; set; }
public TimeSpan? Timeleft { get; set; }
public String Status { get; set; }
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using NzbDrone.Common;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles.Events;
@ -40,7 +41,8 @@ namespace NzbDrone.Api.Series
PathExistsValidator pathExistsValidator,
SeriesPathValidator seriesPathValidator,
SeriesExistsValidator seriesExistsValidator,
DroneFactoryValidator droneFactoryValidator
DroneFactoryValidator droneFactoryValidator,
SeriesAncestorValidator seriesAncestorValidator
)
: base(commandExecutor)
{
@ -59,17 +61,21 @@ namespace NzbDrone.Api.Series
SharedValidator.RuleFor(s => s.QualityProfileId).ValidId();
PutValidator.RuleFor(s => s.Path)
.Cascade(CascadeMode.StopOnFirstFailure)
.IsValidPath()
.SetValidator(rootFolderValidator)
.SetValidator(seriesPathValidator)
.SetValidator(droneFactoryValidator);
SharedValidator.RuleFor(s => s.Path)
.Cascade(CascadeMode.StopOnFirstFailure)
.IsValidPath()
.SetValidator(rootFolderValidator)
.SetValidator(seriesPathValidator)
.SetValidator(droneFactoryValidator)
.SetValidator(seriesAncestorValidator)
.When(s => !s.Path.IsNullOrWhiteSpace());
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.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.Title).NotEmpty();
PostValidator.RuleFor(s => s.TvdbId).GreaterThan(0).SetValidator(seriesExistsValidator);
PutValidator.RuleFor(s => s.Path).IsValidPath();
}
private void PopulateAlternativeTitles(List<SeriesResource> resources)

View File

@ -137,7 +137,7 @@ namespace NzbDrone.Common.Test.DiskProviderTests
}
[Test]
public void move_read_only_file()
public void should_be_able_to_move_read_only_file()
{
var source = GetTempFilePath();
var destination = GetTempFilePath();
@ -151,6 +151,23 @@ namespace NzbDrone.Common.Test.DiskProviderTests
Subject.MoveFile(source, destination);
}
[Test]
public void should_be_able_to_delete_directory_with_read_only_file()
{
var sourceDir = GetTempFilePath();
var source = Path.Combine(sourceDir, "test.txt");
Directory.CreateDirectory(sourceDir);
Subject.WriteAllText(source, "SourceFile");
File.SetAttributes(source, FileAttributes.ReadOnly);
Subject.DeleteFolder(sourceDir, true);
Directory.Exists(sourceDir).Should().BeFalse();
}
[Test]
public void empty_folder_should_return_folder_modified_date()
{

View File

@ -5,32 +5,7 @@ using NzbDrone.Test.Common;
namespace NzbDrone.Common.Test.DiskProviderTests
{
public class IsParentFixture : TestBase
public class IsParentPathFixture : TestBase
{
private string _parent = @"C:\Test".AsOsAgnostic();
[Test]
public void should_return_false_when_not_a_child()
{
var path = @"C:\Another Folder".AsOsAgnostic();
DiskProviderBase.IsParent(_parent, path).Should().BeFalse();
}
[Test]
public void should_return_true_when_folder_is_parent_of_another_folder()
{
var path = @"C:\Test\TV".AsOsAgnostic();
DiskProviderBase.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();
DiskProviderBase.IsParent(_parent, path).Should().BeTrue();
}
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.Diagnostics;
using System.IO;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Common.Test
{
[TestFixture]
public class LevenshteinDistanceFixture : TestBase
{
[TestCase("", "", 0)]
[TestCase("abc", "abc", 0)]
[TestCase("abc", "abcd", 1)]
[TestCase("abcd", "abc", 1)]
[TestCase("abc", "abd", 1)]
[TestCase("abc", "adc", 1)]
[TestCase("abcdefgh", "abcghdef", 4)]
[TestCase("a.b.c.", "abc", 3)]
[TestCase("Agents Of SHIELD", "Marvel's Agents Of S.H.I.E.L.D.", 15)]
[TestCase("Agents of cracked", "Agents of shield", 6)]
[TestCase("ABCxxx", "ABC1xx", 1)]
[TestCase("ABC1xx", "ABCxxx", 1)]
public void LevenshteinDistance(String text, String other, Int32 expected)
{
text.LevenshteinDistance(other).Should().Be(expected);
}
[TestCase("", "", 0)]
[TestCase("abc", "abc", 0)]
[TestCase("abc", "abcd", 1)]
[TestCase("abcd", "abc", 3)]
[TestCase("abc", "abd", 3)]
[TestCase("abc", "adc", 3)]
[TestCase("abcdefgh", "abcghdef", 8)]
[TestCase("a.b.c.", "abc", 0)]
[TestCase("Agents of shield", "Marvel's Agents Of S.H.I.E.L.D.", 9)]
[TestCase("Agents of shield", "Agents of cracked", 14)]
[TestCase("Agents of shield", "the shield", 24)]
[TestCase("ABCxxx", "ABC1xx", 3)]
[TestCase("ABC1xx", "ABCxxx", 3)]
public void LevenshteinDistanceClean(String text, String other, Int32 expected)
{
text.ToLower().LevenshteinDistanceClean(other.ToLower()).Should().Be(expected);
}
}
}

View File

@ -67,6 +67,7 @@
<Compile Include="EnsureTest\PathExtensionFixture.cs" />
<Compile Include="EnvironmentTests\StartupArgumentsFixture.cs" />
<Compile Include="EnvironmentTests\EnvironmentProviderTest.cs" />
<Compile Include="LevenshteinDistanceFixture.cs" />
<Compile Include="ReflectionExtensions.cs" />
<Compile Include="PathExtensionFixture.cs" />
<Compile Include="DiskProviderTests\DiskProviderFixtureBase.cs" />

View File

@ -12,6 +12,7 @@ namespace NzbDrone.Common.Test
[TestFixture]
public class PathExtensionFixture : TestBase
{
private string _parent = @"C:\Test".AsOsAgnostic();
private IAppFolderInfo GetIAppDirectoryInfo()
{
@ -85,6 +86,64 @@ namespace NzbDrone.Common.Test
{
first.AsOsAgnostic().PathEquals(second.AsOsAgnostic()).Should().BeFalse();
}
[Test]
public void should_return_false_when_not_a_child()
{
var path = @"C:\Another Folder".AsOsAgnostic();
_parent.IsParentPath(path).Should().BeFalse();
}
[Test]
public void should_return_true_when_folder_is_parent_of_another_folder()
{
var path = @"C:\Test\TV".AsOsAgnostic();
_parent.IsParentPath(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();
_parent.IsParentPath(path).Should().BeTrue();
}
[TestCase(@"C:\Test\", @"C:\Test\mydir")]
[TestCase(@"C:\Test\", @"C:\Test\mydir\")]
[TestCase(@"C:\Test", @"C:\Test\30.Rock.S01E01.Pilot.avi")]
public void path_should_be_parent(string parentPath, string childPath)
{
parentPath.AsOsAgnostic().IsParentPath(childPath.AsOsAgnostic()).Should().BeTrue();
}
[TestCase(@"C:\Test2\", @"C:\Test")]
[TestCase(@"C:\Test\Test\", @"C:\Test\")]
[TestCase(@"C:\Test\", @"C:\Test")]
[TestCase(@"C:\Test\", @"C:\Test\")]
public void path_should_not_be_parent(string parentPath, string childPath)
{
parentPath.AsOsAgnostic().IsParentPath(childPath.AsOsAgnostic()).Should().BeFalse();
}
[TestCase(@"C:\test\", @"C:\Test\mydir")]
[TestCase(@"C:\test", @"C:\Test\mydir\")]
public void path_should_be_parent_on_windows_only(string parentPath, string childPath)
{
var expectedResult = OsInfo.IsWindows;
parentPath.IsParentPath(childPath).Should().Be(expectedResult);
}
[Test]
[Ignore]
public void should_not_be_parent_when_it_is_grandparent()
{
var path = Path.Combine(_parent, "parent", "child");
_parent.IsParentPath(path).Should().BeFalse();
}
[Test]
public void normalize_path_exception_empty()

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Common
{
public static class ConvertBase32
{
private static string ValidChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
public static byte[] FromBase32String(string str)
{
int numBytes = str.Length * 5 / 8;
byte[] bytes = new Byte[numBytes];
// all UPPERCASE chars
str = str.ToUpper();
int bitBuffer = 0;
int bitBufferCount = 0;
int index = 0;
for (int i = 0; i < str.Length;i++ )
{
bitBuffer = (bitBuffer << 5) | ValidChars.IndexOf(str[i]);
bitBufferCount += 5;
if (bitBufferCount >= 8)
{
bitBufferCount -= 8;
bytes[index++] = (byte)(bitBuffer >> bitBufferCount);
}
}
return bytes;
}
}
}

View File

@ -25,45 +25,17 @@ namespace NzbDrone.Common.Disk
public abstract void SetPermissions(string path, string mask, string user, string group);
public abstract long? GetTotalSize(string path);
public static string GetRelativePath(string parentPath, string childPath)
public DateTime FolderGetCreationTimeUtc(string path)
{
if (!IsParent(parentPath, childPath))
{
throw new NotParentException("{0} is not a child of {1}", childPath, parentPath);
}
CheckFolderExists(path);
return childPath.Substring(parentPath.Length).Trim(Path.DirectorySeparatorChar);
}
public static bool IsParent(string parentPath, string childPath)
{
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar);
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar);
var parent = new DirectoryInfo(parentPath);
var child = new DirectoryInfo(childPath);
while (child.Parent != null)
{
if (child.Parent.FullName == parent.FullName)
{
return true;
}
child = child.Parent;
}
return false;
return new DirectoryInfo(path).CreationTimeUtc;
}
public DateTime FolderGetLastWrite(string path)
{
Ensure.That(path, () => path).IsValidPath();
if (!FolderExists(path))
{
throw new DirectoryNotFoundException("Directory doesn't exist. " + path);
}
CheckFolderExists(path);
var dirFiles = GetFiles(path, SearchOption.AllDirectories).ToList();
@ -76,21 +48,38 @@ namespace NzbDrone.Common.Disk
.Max(c => c.LastWriteTimeUtc);
}
public DateTime FileGetCreationTimeUtc(string path)
{
CheckFileExists(path);
return new FileInfo(path).CreationTimeUtc;
}
public DateTime FileGetLastWrite(string path)
{
PathEnsureFileExists(path);
CheckFileExists(path);
return new FileInfo(path).LastWriteTime;
}
public DateTime FileGetLastWriteUtc(string path)
{
PathEnsureFileExists(path);
CheckFileExists(path);
return new FileInfo(path).LastWriteTimeUtc;
}
private void PathEnsureFileExists(string path)
private void CheckFolderExists(string path)
{
Ensure.That(path, () => path).IsValidPath();
if (!FolderExists(path))
{
throw new DirectoryNotFoundException("Directory doesn't exist. " + path);
}
}
private void CheckFileExists(string path)
{
Ensure.That(path, () => path).IsValidPath();
@ -286,6 +275,9 @@ namespace NzbDrone.Common.Disk
{
Ensure.That(path, () => path).IsValidPath();
var files = Directory.GetFiles(path, "*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
Array.ForEach(files, RemoveReadOnly);
Directory.Delete(path, recursive);
}

View File

@ -11,7 +11,9 @@ namespace NzbDrone.Common.Disk
void InheritFolderPermissions(string filename);
void SetPermissions(string path, string mask, string user, string group);
long? GetTotalSize(string path);
DateTime FolderGetCreationTimeUtc(string path);
DateTime FolderGetLastWrite(string path);
DateTime FileGetCreationTimeUtc(string path);
DateTime FileGetLastWrite(string path);
DateTime FileGetLastWriteUtc(string path);
void EnsureFolder(string path);

View File

@ -24,11 +24,14 @@ namespace NzbDrone.Common.EnvironmentInfo
if (!IsMono)
{
Os = Os.Windows;
}
PathStringComparison = StringComparison.OrdinalIgnoreCase;
}
else
{
Os = IsOsx ? Os.Osx : Os.Linux;
PathStringComparison = StringComparison.Ordinal;
}
}
@ -40,6 +43,7 @@ namespace NzbDrone.Common.EnvironmentInfo
public static bool IsWindows { get; private set; }
public static Os Os { get; private set; }
public static DayOfWeek FirstDayOfWeek { get; private set; }
public static StringComparison PathStringComparison { get; private set; }
//Borrowed from: https://github.com/jpobst/Pinta/blob/master/Pinta.Core/Managers/SystemManager.cs
//From Managed.Windows.Forms/XplatUI

View File

@ -84,6 +84,7 @@ namespace NzbDrone.Common.Http
public Stream DownloadStream(string url, NetworkCredential credential = null)
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
request.UserAgent = _userAgent;
request.Timeout = 20 * 1000;

View File

@ -11,6 +11,7 @@ namespace NzbDrone.Common.Http
if (request is HttpWebRequest)
{
((HttpWebRequest)request).KeepAlive = false;
((HttpWebRequest)request).ServicePoint.Expect100Continue = false;
}
return request;

View File

@ -22,7 +22,8 @@ namespace NzbDrone.Common.Instrumentation
RegisterDebugger();
}
RegisterExceptron();
//Disabling for now - until its fixed or we yank it out
//RegisterExceptron();
if (updateApp)
{

View File

@ -0,0 +1,55 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.SharpZipLib.Zip;
namespace NzbDrone.Common
{
public static class LevenstheinExtensions
{
public static Int32 LevenshteinDistance(this String text, String other, Int32 costInsert = 1, Int32 costDelete = 1, Int32 costSubstitute = 1)
{
if (text == other) return 0;
if (text.Length == 0) return other.Length * costInsert;
if (other.Length == 0) return text.Length * costDelete;
Int32[] matrix = new Int32[other.Length + 1];
for (var i = 1; i < matrix.Length; i++)
{
matrix[i] = i * costInsert;
}
for (var i = 0; i < text.Length; i++)
{
Int32 topLeft = matrix[0];
matrix[0] = matrix[0] + costDelete;
for (var j = 0; j < other.Length; j++)
{
Int32 top = matrix[j];
Int32 left = matrix[j + 1];
var sumIns = top + costInsert;
var sumDel = left + costDelete;
var sumSub = topLeft + (text[i] == other[j] ? 0 : costSubstitute);
topLeft = matrix[j + 1];
matrix[j + 1] = Math.Min(Math.Min(sumIns, sumDel), sumSub);
}
}
return matrix[other.Length];
}
public static Int32 LevenshteinDistanceClean(this String expected, String other)
{
expected = expected.ToLower().Replace(".", "");
other = other.ToLower().Replace(".", "");
return expected.LevenshteinDistance(other, 1, 3, 3);
}
}
}

View File

@ -60,6 +60,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ArchiveProvider.cs" />
<Compile Include="ConvertBase32.cs" />
<Compile Include="Cache\Cached.cs" />
<Compile Include="Cache\CacheManager.cs" />
<Compile Include="Cache\ICached.cs" />
@ -113,6 +114,7 @@
<Compile Include="Serializer\IntConverter.cs" />
<Compile Include="Services.cs" />
<Compile Include="Extensions\StreamExtensions.cs" />
<Compile Include="LevenstheinExtensions.cs" />
<Compile Include="TPL\LimitedConcurrencyLevelTaskScheduler.cs" />
<Compile Include="Security\IgnoreCertErrorPolicy.cs" />
<Compile Include="StringExtensions.cs" />

View File

@ -39,14 +39,39 @@ namespace NzbDrone.Common
public static bool PathEquals(this string firstPath, string secondPath)
{
if (OsInfo.IsMono)
if (firstPath.Equals(secondPath, OsInfo.PathStringComparison)) return true;
return String.Equals(firstPath.CleanFilePath(), secondPath.CleanFilePath(), OsInfo.PathStringComparison);
}
public static string GetRelativePath(this string parentPath, string childPath)
{
if (!parentPath.IsParentPath(childPath))
{
if (firstPath.Equals(secondPath)) return true;
return String.Equals(firstPath.CleanFilePath(), secondPath.CleanFilePath());
throw new Exceptions.NotParentException("{0} is not a child of {1}", childPath, parentPath);
}
if (firstPath.Equals(secondPath, StringComparison.OrdinalIgnoreCase)) return true;
return String.Equals(firstPath.CleanFilePath(), secondPath.CleanFilePath(), StringComparison.OrdinalIgnoreCase);
return childPath.Substring(parentPath.Length).Trim(Path.DirectorySeparatorChar);
}
public static bool IsParentPath(this string parentPath, string childPath)
{
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar);
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar);
var parent = new DirectoryInfo(parentPath);
var child = new DirectoryInfo(childPath);
while (child.Parent != null)
{
if (child.Parent.FullName.Equals(parent.FullName, OsInfo.PathStringComparison))
{
return true;
}
child = child.Parent;
}
return false;
}
private static readonly Regex WindowsPathWithDriveRegex = new Regex(@"^[a-zA-Z]:\\", RegexOptions.Compiled);

View File

@ -14,6 +14,8 @@ namespace NzbDrone.Core.Test.Configuration
public void SetUp()
{
Mocker.SetConstant<IConfigRepository>(Mocker.Resolve<ConfigRepository>());
Db.All<Config>().ForEach(Db.Delete);
}
[Test]

View File

@ -67,7 +67,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 3)).Returns<QualityModel>(null);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(c => c.GetDownloadClient()).Returns(Mocker.GetMock<IDownloadClient>().Object);
.Setup(c => c.GetDownloadClients())
.Returns(new IDownloadClient[] { Mocker.GetMock<IDownloadClient>().Object });
}
private void WithFirstReportUpgradable()
@ -83,7 +84,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private void GivenSabnzbdDownloadClient()
{
Mocker.GetMock<IProvideDownloadClient>()
.Setup(c => c.GetDownloadClient()).Returns(Mocker.Resolve<Sabnzbd>());
.Setup(c => c.GetDownloadClients())
.Returns(new IDownloadClient[] { Mocker.Resolve<Sabnzbd>() });
}
private void GivenMostRecentForEpisode(HistoryEventType eventType)

View File

@ -9,6 +9,7 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Queue;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
@ -18,7 +19,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private Series _series;
private Episode _episode;
private RemoteEpisode _remoteEpisode;
private Mock<IDownloadClient> _downloadClient;
private Series _otherSeries;
private Episode _otherEpisode;
@ -50,34 +50,30 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(r => r.Episodes = new List<Episode> { _episode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD)})
.Build();
_downloadClient = Mocker.GetMock<IDownloadClient>();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClient())
.Returns(_downloadClient.Object);
}
private void GivenEmptyQueue()
{
_downloadClient.Setup(s => s.GetQueue())
.Returns(new List<QueueItem>());
Mocker.GetMock<IQueueService>()
.Setup(s => s.GetQueue())
.Returns(new List<Queue.Queue>());
}
private void GivenQueue(IEnumerable<RemoteEpisode> remoteEpisodes)
{
var queue = new List<QueueItem>();
var queue = new List<Queue.Queue>();
foreach (var remoteEpisode in remoteEpisodes)
{
queue.Add(new QueueItem
{
RemoteEpisode = remoteEpisode
queue.Add(new Queue.Queue
{
RemoteEpisode = remoteEpisode
});
}
_downloadClient.Setup(s => s.GetQueue())
.Returns(queue);
Mocker.GetMock<IQueueService>()
.Setup(s => s.GetQueue())
.Returns(queue);
}
[Test]

View File

@ -0,0 +1,439 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.History;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Test.Common;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.Download
{
[TestFixture]
public class CompletedDownloadServiceFixture : CoreTest<DownloadTrackingService>
{
private List<DownloadClientItem> _completed;
[SetUp]
public void Setup()
{
_completed = Builder<DownloadClientItem>.CreateListOfSize(1)
.All()
.With(h => h.Status = DownloadItemStatus.Completed)
.With(h => h.OutputPath = @"C:\DropFolder\MyDownload".AsOsAgnostic())
.With(h => h.RemoteEpisode = new RemoteEpisode
{
Episodes = new List<Episode> { new Episode { Id = 1 } }
})
.Build()
.ToList();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(c => c.GetDownloadClients())
.Returns( new IDownloadClient[] { Mocker.GetMock<IDownloadClient>().Object });
Mocker.GetMock<IDownloadClient>()
.SetupGet(c => c.Definition)
.Returns(new Core.Download.DownloadClientDefinition { Id = 1, Name = "testClient" });
Mocker.GetMock<IConfigService>()
.SetupGet(s => s.EnableCompletedDownloadHandling)
.Returns(true);
Mocker.GetMock<IConfigService>()
.SetupGet(s => s.RemoveCompletedDownloads)
.Returns(true);
Mocker.GetMock<IHistoryService>()
.Setup(s => s.Failed())
.Returns(new List<History.History>());
Mocker.SetConstant<ICompletedDownloadService>(Mocker.Resolve<CompletedDownloadService>());
}
private void GivenNoGrabbedHistory()
{
Mocker.GetMock<IHistoryService>()
.Setup(s => s.Grabbed())
.Returns(new List<History.History>());
}
private void GivenGrabbedHistory(List<History.History> history)
{
Mocker.GetMock<IHistoryService>()
.Setup(s => s.Grabbed())
.Returns(history);
}
private void GivenNoImportedHistory()
{
Mocker.GetMock<IHistoryService>()
.Setup(s => s.Imported())
.Returns(new List<History.History>());
}
private void GivenImportedHistory(List<History.History> importedHistory)
{
Mocker.GetMock<IHistoryService>()
.Setup(s => s.Imported())
.Returns(importedHistory);
}
private void GivenCompletedDownloadClientHistory(bool hasStorage = true)
{
Mocker.GetMock<IDownloadClient>()
.Setup(s => s.GetItems())
.Returns(_completed);
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.FolderExists(It.IsAny<string>()))
.Returns(hasStorage);
}
private void GivenCompletedImport()
{
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
.Returns(new List<Core.MediaFiles.EpisodeImport.ImportDecision>() { new Core.MediaFiles.EpisodeImport.ImportDecision(null) });
}
private void GivenFailedImport()
{
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
.Returns(new List<Core.MediaFiles.EpisodeImport.ImportDecision>());
}
private void VerifyNoImports()
{
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Verify(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()), Times.Never());
}
private void VerifyImports()
{
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Verify(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()), Times.Once());
}
[Test]
public void should_process_if_matching_history_is_not_found_but_category_specified()
{
_completed.First().Category = "tv";
GivenCompletedDownloadClientHistory();
GivenNoGrabbedHistory();
GivenNoImportedHistory();
GivenCompletedImport();
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyImports();
}
[Test]
public void should_not_process_if_matching_history_is_not_found_and_no_category_specified()
{
_completed.First().Category = null;
GivenCompletedDownloadClientHistory();
GivenNoGrabbedHistory();
GivenNoImportedHistory();
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoImports();
}
[Test]
public void should_not_process_if_grabbed_history_contains_null_downloadclient_id()
{
_completed.First().Category = null;
GivenCompletedDownloadClientHistory();
var historyGrabbed = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
historyGrabbed.First().Data.Add("downloadClient", "SabnzbdClient");
historyGrabbed.First().Data.Add("downloadClientId", null);
GivenGrabbedHistory(historyGrabbed);
GivenNoImportedHistory();
GivenFailedImport();
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoImports();
}
[Test]
public void should_process_if_failed_history_contains_null_downloadclient_id()
{
GivenCompletedDownloadClientHistory();
var historyGrabbed = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
historyGrabbed.First().Data.Add("downloadClient", "SabnzbdClient");
historyGrabbed.First().Data.Add("downloadClientId", _completed.First().DownloadClientId);
GivenGrabbedHistory(historyGrabbed);
var historyImported = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
historyImported.First().Data.Add("downloadClient", "SabnzbdClient");
historyImported.First().Data.Add("downloadClientId", null);
GivenImportedHistory(historyImported);
GivenCompletedImport();
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyImports();
}
[Test]
public void should_not_process_if_already_added_to_history_as_imported()
{
GivenCompletedDownloadClientHistory();
var history = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
GivenGrabbedHistory(history);
GivenImportedHistory(history);
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId);
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoImports();
}
[Test]
public void should_process_if_not_already_in_imported_history()
{
GivenCompletedDownloadClientHistory();
var history = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
GivenGrabbedHistory(history);
GivenNoImportedHistory();
GivenCompletedImport();
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId);
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyImports();
}
[Test]
public void should_not_process_if_storage_directory_does_not_exist()
{
GivenCompletedDownloadClientHistory(false);
var history = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
GivenGrabbedHistory(history);
GivenNoImportedHistory();
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId);
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoImports();
}
[Test]
public void should_not_process_if_storage_directory_in_drone_factory()
{
GivenCompletedDownloadClientHistory(true);
var history = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
GivenGrabbedHistory(history);
GivenNoImportedHistory();
Mocker.GetMock<IConfigService>()
.SetupGet(v => v.DownloadedEpisodesFolder)
.Returns(@"C:\DropFolder".AsOsAgnostic());
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId);
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoImports();
}
[Test]
public void should_process_as_already_imported_if_drone_factory_import_history_exists()
{
GivenCompletedDownloadClientHistory(false);
_completed.Clear();
_completed.AddRange(Builder<DownloadClientItem>.CreateListOfSize(2)
.All()
.With(h => h.Status = DownloadItemStatus.Completed)
.With(h => h.OutputPath = @"C:\DropFolder\MyDownload".AsOsAgnostic())
.With(h => h.RemoteEpisode = new RemoteEpisode
{
Episodes = new List<Episode> { new Episode { Id = 1 } }
})
.Build());
var grabbedHistory = Builder<History.History>.CreateListOfSize(2)
.All()
.With(d => d.Data["downloadClient"] = "SabnzbdClient")
.TheFirst(1)
.With(d => d.Data["downloadClientId"] = _completed.First().DownloadClientId)
.With(d => d.SourceTitle = "Droned.S01E01.720p-LAZY")
.TheLast(1)
.With(d => d.Data["downloadClientId"] = _completed.Last().DownloadClientId)
.With(d => d.SourceTitle = "Droned.S01E01.Proper.720p-LAZY")
.Build()
.ToList();
var importedHistory = Builder<History.History>.CreateListOfSize(2)
.All()
.With(d => d.EpisodeId = 1)
.TheFirst(1)
.With(d => d.Data["droppedPath"] = @"C:\mydownload\Droned.S01E01.720p-LAZY\lzy-dr101.mkv".AsOsAgnostic())
.TheLast(1)
.With(d => d.Data["droppedPath"] = @"C:\mydownload\Droned.S01E01.Proper.720p-LAZY\lzy-dr101.mkv".AsOsAgnostic())
.Build()
.ToList();
GivenGrabbedHistory(grabbedHistory);
GivenImportedHistory(importedHistory);
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoImports();
Mocker.GetMock<IHistoryService>()
.Verify(v => v.UpdateHistoryData(It.IsAny<int>(), It.IsAny<Dictionary<String, String>>()), Times.Exactly(2));
}
[Test]
public void should_not_remove_if_config_disabled()
{
GivenCompletedDownloadClientHistory();
var history = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
GivenGrabbedHistory(history);
GivenNoImportedHistory();
GivenCompletedImport();
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId);
Mocker.GetMock<IConfigService>()
.SetupGet(s => s.RemoveCompletedDownloads)
.Returns(false);
Subject.Execute(new CheckForFinishedDownloadCommand());
Mocker.GetMock<IDiskProvider>()
.Verify(c => c.DeleteFolder(It.IsAny<string>(), true), Times.Never());
}
[Test]
public void should_not_remove_while_readonly()
{
GivenCompletedDownloadClientHistory();
var history = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
GivenGrabbedHistory(history);
GivenNoImportedHistory();
GivenCompletedImport();
_completed.First().IsReadOnly = true;
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId);
Subject.Execute(new CheckForFinishedDownloadCommand());
Mocker.GetMock<IDiskProvider>()
.Verify(c => c.DeleteFolder(It.IsAny<string>(), true), Times.Never());
}
[Test]
public void should_not_remove_if_imported_failed()
{
GivenCompletedDownloadClientHistory();
var history = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
GivenGrabbedHistory(history);
GivenNoImportedHistory();
GivenFailedImport();
_completed.First().IsReadOnly = true;
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId);
Subject.Execute(new CheckForFinishedDownloadCommand());
Mocker.GetMock<IDiskProvider>()
.Verify(c => c.DeleteFolder(It.IsAny<string>(), true), Times.Never());
}
[Test]
public void should_remove_if_imported()
{
GivenCompletedDownloadClientHistory();
var history = Builder<History.History>.CreateListOfSize(1)
.Build()
.ToList();
GivenGrabbedHistory(history);
GivenNoImportedHistory();
GivenCompletedImport();
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId);
Subject.Execute(new CheckForFinishedDownloadCommand());
Mocker.GetMock<IDiskProvider>()
.Verify(c => c.DeleteFolder(It.IsAny<string>(), true), Times.Once());
}
}
}

View File

@ -0,0 +1,118 @@
using System.IO;
using System.Net;
using System.Linq;
using Moq;
using NUnit.Framework;
using FluentAssertions;
using NzbDrone.Test.Common;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Download.Clients.UsenetBlackhole;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
{
[TestFixture]
public class UsenetBlackholeFixture : DownloadClientFixtureBase<UsenetBlackhole>
{
protected string _completedDownloadFolder;
protected string _blackholeFolder;
protected string _filePath;
[SetUp]
public void Setup()
{
_completedDownloadFolder = @"c:\blackhole\completed".AsOsAgnostic();
_blackholeFolder = @"c:\blackhole\nzb".AsOsAgnostic();
_filePath = (@"c:\blackhole\nzb\" + _title + ".nzb").AsOsAgnostic();
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new UsenetBlackholeSettings
{
NzbFolder = _blackholeFolder,
WatchFolder = _completedDownloadFolder
};
}
protected void WithSuccessfulDownload()
{
}
protected void WithFailedDownload()
{
Mocker.GetMock<IHttpProvider>()
.Setup(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()))
.Throws(new WebException());
}
protected void GivenCompletedItem()
{
var targetDir = Path.Combine(_completedDownloadFolder, _title);
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetDirectories(_completedDownloadFolder))
.Returns(new[] { targetDir });
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
.Returns(new[] { Path.Combine(_completedDownloadFolder, "somefile.mkv") });
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetFileSize(It.IsAny<string>()))
.Returns(1000000);
}
[Test]
public void completed_download_should_have_required_properties()
{
GivenCompletedItem();
var result = Subject.GetItems().Single();
VerifyCompleted(result);
}
[Test]
public void Download_should_download_file_if_it_doesnt_exist()
{
var remoteEpisode = CreateRemoteEpisode();
Subject.Download(remoteEpisode);
Mocker.GetMock<IHttpProvider>().Verify(c => c.DownloadFile(_downloadUrl, _filePath), Times.Once());
}
[Test]
public void Download_should_replace_illegal_characters_in_title()
{
var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]";
var expectedFilename = Path.Combine(_blackholeFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV]" + Path.GetExtension(_filePath));
var remoteEpisode = CreateRemoteEpisode();
remoteEpisode.Release.Title = illegalTitle;
Subject.Download(remoteEpisode);
Mocker.GetMock<IHttpProvider>().Verify(c => c.DownloadFile(It.IsAny<string>(), expectedFilename), Times.Once());
}
[Test]
public void GetItems_should_considered_locked_files_downloading()
{
GivenCompletedItem();
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.IsFileLocked(It.IsAny<string>()))
.Returns(true);
var result = Subject.GetItems().Single();
result.Status.Should().Be(DownloadItemStatus.Downloading);
}
}
}

View File

@ -1,74 +0,0 @@
using System.IO;
using System.Net;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Download.Clients.Blackhole;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Download.DownloadClientTests
{
[TestFixture]
public class BlackholeProviderFixture : CoreTest<Blackhole>
{
private const string _nzbUrl = "http://www.nzbs.com/url";
private const string _title = "some_nzb_title";
private string _blackHoleFolder;
private string _nzbPath;
private RemoteEpisode _remoteEpisode;
[SetUp]
public void Setup()
{
_blackHoleFolder = @"c:\nzb\blackhole\".AsOsAgnostic();
_nzbPath = @"c:\nzb\blackhole\some_nzb_title.nzb".AsOsAgnostic();
_remoteEpisode = new RemoteEpisode();
_remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Release.Title = _title;
_remoteEpisode.Release.DownloadUrl = _nzbUrl;
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new FolderSettings
{
Folder = _blackHoleFolder
};
}
private void WithExistingFile()
{
Mocker.GetMock<IDiskProvider>().Setup(c => c.FileExists(_nzbPath)).Returns(true);
}
private void WithFailedDownload()
{
Mocker.GetMock<IHttpProvider>().Setup(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>())).Throws(new WebException());
}
[Test]
public void DownloadNzb_should_download_file_if_it_doesnt_exist()
{
Subject.DownloadNzb(_remoteEpisode);
Mocker.GetMock<IHttpProvider>().Verify(c => c.DownloadFile(_nzbUrl, _nzbPath), Times.Once());
}
[Test]
public void should_replace_illegal_characters_in_title()
{
var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]";
var expectedFilename = Path.Combine(_blackHoleFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb");
_remoteEpisode.Release.Title = illegalTitle;
Subject.DownloadNzb(_remoteEpisode);
Mocker.GetMock<IHttpProvider>().Verify(c => c.DownloadFile(It.IsAny<string>(), expectedFilename), Times.Once());
}
}
}

View File

@ -0,0 +1,111 @@
using System;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using FluentAssertions;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Download;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.Test.Download.DownloadClientTests
{
public abstract class DownloadClientFixtureBase<TSubject> : CoreTest<TSubject>
where TSubject : class, IDownloadClient
{
protected readonly string _title = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE";
protected readonly string _downloadUrl = "http://somewhere.com/Droned.S01E01.Pilot.1080p.WEB-DL-DRONE.ext";
[SetUp]
public void SetupBase()
{
Mocker.GetMock<IConfigService>()
.SetupGet(s => s.DownloadClientHistoryLimit)
.Returns(30);
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), null))
.Returns(CreateRemoteEpisode());
}
protected virtual RemoteEpisode CreateRemoteEpisode()
{
var remoteEpisode = new RemoteEpisode();
remoteEpisode.Release = new ReleaseInfo();
remoteEpisode.Release.Title = _title;
remoteEpisode.Release.DownloadUrl = _downloadUrl;
remoteEpisode.Release.DownloadProtocol = Subject.Protocol;
remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo();
remoteEpisode.ParsedEpisodeInfo.FullSeason = false;
remoteEpisode.Episodes = new List<Episode>();
remoteEpisode.Series = new Series();
return remoteEpisode;
}
protected void VerifyIdentifiable(DownloadClientItem downloadClientItem)
{
downloadClientItem.DownloadClient.Should().Be(Subject.Definition.Name);
downloadClientItem.DownloadClientId.Should().NotBeNullOrEmpty();
downloadClientItem.Title.Should().NotBeNullOrEmpty();
downloadClientItem.RemoteEpisode.Should().NotBeNull();
}
protected void VerifyQueued(DownloadClientItem downloadClientItem)
{
VerifyIdentifiable(downloadClientItem);
downloadClientItem.RemainingSize.Should().NotBe(0);
//downloadClientItem.RemainingTime.Should().NotBe(TimeSpan.Zero);
//downloadClientItem.OutputPath.Should().NotBeNullOrEmpty();
downloadClientItem.Status.Should().Be(DownloadItemStatus.Queued);
}
protected void VerifyPaused(DownloadClientItem downloadClientItem)
{
VerifyIdentifiable(downloadClientItem);
downloadClientItem.RemainingSize.Should().NotBe(0);
//downloadClientItem.RemainingTime.Should().NotBe(TimeSpan.Zero);
//downloadClientItem.OutputPath.Should().NotBeNullOrEmpty();
downloadClientItem.Status.Should().Be(DownloadItemStatus.Paused);
}
protected void VerifyDownloading(DownloadClientItem downloadClientItem)
{
VerifyIdentifiable(downloadClientItem);
downloadClientItem.RemainingSize.Should().NotBe(0);
//downloadClientItem.RemainingTime.Should().NotBe(TimeSpan.Zero);
//downloadClientItem.OutputPath.Should().NotBeNullOrEmpty();
downloadClientItem.Status.Should().Be(DownloadItemStatus.Downloading);
}
protected void VerifyCompleted(DownloadClientItem downloadClientItem)
{
VerifyIdentifiable(downloadClientItem);
downloadClientItem.Title.Should().NotBeNullOrEmpty();
downloadClientItem.RemainingSize.Should().Be(0);
downloadClientItem.RemainingTime.Should().Be(TimeSpan.Zero);
//downloadClientItem.OutputPath.Should().NotBeNullOrEmpty();
downloadClientItem.Status.Should().Be(DownloadItemStatus.Completed);
}
protected void VerifyFailed(DownloadClientItem downloadClientItem)
{
VerifyIdentifiable(downloadClientItem);
downloadClientItem.Status.Should().Be(DownloadItemStatus.Failed);
}
}
}

View File

@ -1,61 +0,0 @@
using System;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.Nzbget;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
{
public class DownloadNzbFixture : CoreTest<Nzbget>
{
private const string _url = "http://www.nzbdrone.com";
private const string _title = "30.Rock.S01E01.Pilot.720p.hdtv";
private RemoteEpisode _remoteEpisode;
[SetUp]
public void Setup()
{
_remoteEpisode = new RemoteEpisode();
_remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Release.Title = _title;
_remoteEpisode.Release.DownloadUrl = _url;
_remoteEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT))
.Build()
.ToList();
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new NzbgetSettings
{
Host = "localhost",
Port = 6789,
Username = "nzbget",
Password = "pass",
TvCategory = "tv",
RecentTvPriority = (int)NzbgetPriority.High
};
}
[Test]
public void should_add_item_to_queue()
{
Mocker.GetMock<INzbgetProxy>()
.Setup(s => s.DownloadNzb(It.IsAny<Stream>(), It.IsAny<String>(), It.IsAny<String>(), It.IsAny<Int32>(), It.IsAny<NzbgetSettings>()))
.Returns("id");
Subject.DownloadNzb(_remoteEpisode);
Mocker.GetMock<INzbgetProxy>()
.Verify(v => v.DownloadNzb(It.IsAny<Stream>(), It.IsAny<String>(), It.IsAny<String>(), It.IsAny<Int32>(), It.IsAny<NzbgetSettings>()), Times.Once());
}
}
}

View File

@ -0,0 +1,227 @@
using System;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.Nzbget;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using System.Collections.Generic;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
{
[TestFixture]
public class NzbgetFixture : DownloadClientFixtureBase<Nzbget>
{
private NzbgetQueueItem _queued;
private NzbgetHistoryItem _failed;
private NzbgetHistoryItem _completed;
[SetUp]
public void Setup()
{
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new NzbgetSettings
{
Host = "192.168.5.55",
Port = 2222,
Username = "admin",
Password = "pass",
TvCategory = "tv",
RecentTvPriority = (int)NzbgetPriority.High
};
_queued = new NzbgetQueueItem
{
FileSizeLo = 1000,
RemainingSizeLo = 10,
Category = "tv",
NzbName = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE",
Parameters = new List<NzbgetParameter> { new NzbgetParameter { Name = "drone", Value = "id" } }
};
_failed = new NzbgetHistoryItem
{
FileSizeLo = 1000,
Category = "tv",
Name = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE",
DestDir = "somedirectory",
Parameters = new List<NzbgetParameter> { new NzbgetParameter { Name = "drone", Value = "id" } },
ParStatus = "Some Error",
UnpackStatus = "NONE",
MoveStatus = "NONE",
ScriptStatus = "NONE",
DeleteStatus = "NONE",
MarkStatus = "NONE"
};
_completed = new NzbgetHistoryItem
{
FileSizeLo = 1000,
Category = "tv",
Name = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE",
DestDir = "somedirectory",
Parameters = new List<NzbgetParameter> { new NzbgetParameter { Name = "drone", Value = "id" } },
ParStatus = "SUCCESS",
UnpackStatus = "NONE",
MoveStatus = "SUCCESS",
ScriptStatus = "NONE",
DeleteStatus = "NONE",
MarkStatus = "NONE"
};
Mocker.GetMock<INzbgetProxy>()
.Setup(s => s.GetGlobalStatus(It.IsAny<NzbgetSettings>()))
.Returns(new NzbgetGlobalStatus
{
DownloadRate = 7000000
});
}
protected void WithFailedDownload()
{
Mocker.GetMock<INzbgetProxy>()
.Setup(s => s.DownloadNzb(It.IsAny<Stream>(), It.IsAny<String>(), It.IsAny<String>(), It.IsAny<int>(), It.IsAny<NzbgetSettings>()))
.Returns((String)null);
}
protected void WithSuccessfulDownload()
{
Mocker.GetMock<INzbgetProxy>()
.Setup(s => s.DownloadNzb(It.IsAny<Stream>(), It.IsAny<String>(), It.IsAny<String>(), It.IsAny<int>(), It.IsAny<NzbgetSettings>()))
.Returns(Guid.NewGuid().ToString().Replace("-", ""));
}
protected virtual void WithQueue(NzbgetQueueItem queue)
{
var list = new List<NzbgetQueueItem>();
if (queue != null)
{
list.Add(queue);
}
Mocker.GetMock<INzbgetProxy>()
.Setup(s => s.GetQueue(It.IsAny<NzbgetSettings>()))
.Returns(list);
Mocker.GetMock<INzbgetProxy>()
.Setup(s => s.GetPostQueue(It.IsAny<NzbgetSettings>()))
.Returns(new List<NzbgetPostQueueItem>());
}
protected virtual void WithHistory(NzbgetHistoryItem history)
{
var list = new List<NzbgetHistoryItem>();
if (history != null)
{
list.Add(history);
}
Mocker.GetMock<INzbgetProxy>()
.Setup(s => s.GetHistory(It.IsAny<NzbgetSettings>()))
.Returns(list);
}
[Test]
public void GetItems_should_return_no_items_when_queue_is_empty()
{
WithQueue(null);
WithHistory(null);
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void queued_item_should_have_required_properties()
{
_queued.ActiveDownloads = 0;
WithQueue(_queued);
WithHistory(null);
var result = Subject.GetItems().Single();
VerifyQueued(result);
}
[Test]
public void paused_item_should_have_required_properties()
{
_queued.PausedSizeLo = _queued.RemainingSizeLo;
WithQueue(_queued);
WithHistory(null);
var result = Subject.GetItems().Single();
VerifyPaused(result);
}
[Test]
public void downloading_item_should_have_required_properties()
{
_queued.ActiveDownloads = 1;
WithQueue(_queued);
WithHistory(null);
var result = Subject.GetItems().Single();
VerifyDownloading(result);
}
[Test]
public void completed_download_should_have_required_properties()
{
WithQueue(null);
WithHistory(_completed);
var result = Subject.GetItems().Single();
VerifyCompleted(result);
}
[Test]
public void failed_item_should_have_required_properties()
{
WithQueue(null);
WithHistory(_failed);
var result = Subject.GetItems().Single();
VerifyFailed(result);
}
[Test]
public void Download_should_return_unique_id()
{
WithSuccessfulDownload();
var remoteEpisode = CreateRemoteEpisode();
var id = Subject.Download(remoteEpisode);
id.Should().NotBeNullOrEmpty();
}
[Test]
public void GetItems_should_ignore_downloads_from_other_categories()
{
_completed.Category = "mycat";
WithQueue(null);
WithHistory(_completed);
var items = Subject.GetItems();
items.Should().BeEmpty();
}
}
}

View File

@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.Nzbget;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
{
public class QueueFixture : CoreTest<Nzbget>
{
private List<NzbgetQueueItem> _queue;
[SetUp]
public void Setup()
{
_queue = Builder<NzbgetQueueItem>.CreateListOfSize(5)
.All()
.With(q => q.NzbName = "30.Rock.S01E01.Pilot.720p.hdtv.nzb")
.With(q => q.Parameters = new List<NzbgetParameter>
{
new NzbgetParameter { Name = "drone", Value = "id" }
})
.Build()
.ToList();
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new NzbgetSettings
{
Host = "localhost",
Port = 6789,
Username = "nzbget",
Password = "pass",
TvCategory = "tv",
RecentTvPriority = (int)NzbgetPriority.High
};
}
private void WithFullQueue()
{
Mocker.GetMock<INzbgetProxy>()
.Setup(s => s.GetQueue(It.IsAny<NzbgetSettings>()))
.Returns(_queue);
}
private void WithEmptyQueue()
{
Mocker.GetMock<INzbgetProxy>()
.Setup(s => s.GetQueue(It.IsAny<NzbgetSettings>()))
.Returns(new List<NzbgetQueueItem>());
}
[Test]
public void should_return_no_items_when_queue_is_empty()
{
WithEmptyQueue();
Subject.GetQueue()
.Should()
.BeEmpty();
}
[Test]
public void should_return_item_when_queue_has_item()
{
WithFullQueue();
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), 0, null))
.Returns(new RemoteEpisode {Series = new Series()});
Subject.GetQueue()
.Should()
.HaveCount(_queue.Count);
}
}
}

View File

@ -45,9 +45,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
_remoteEpisode.ParsedEpisodeInfo.FullSeason = false;
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new FolderSettings
Subject.Definition.Settings = new PneumaticSettings
{
Folder = _pneumaticFolder
NzbFolder = _pneumaticFolder
};
}
@ -64,7 +64,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
[Test]
public void should_download_file_if_it_doesnt_exist()
{
Subject.DownloadNzb(_remoteEpisode);
Subject.Download(_remoteEpisode);
Mocker.GetMock<IHttpProvider>().Verify(c => c.DownloadFile(_nzbUrl, _nzbPath), Times.Once());
}
@ -75,7 +75,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
{
WithFailedDownload();
Assert.Throws<WebException>(() => Subject.DownloadNzb(_remoteEpisode));
Assert.Throws<WebException>(() => Subject.Download(_remoteEpisode));
}
[Test]
@ -84,7 +84,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
_remoteEpisode.Release.Title = "30 Rock - Season 1";
_remoteEpisode.ParsedEpisodeInfo.FullSeason = true;
Assert.Throws<NotImplementedException>(() => Subject.DownloadNzb(_remoteEpisode));
Assert.Throws<NotSupportedException>(() => Subject.Download(_remoteEpisode));
}
[Test]
public void should_throw_item_is_removed()
{
Assert.Throws<NotSupportedException>(() => Subject.RemoveItem(""));
}
[Test]
@ -94,7 +100,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
var expectedFilename = Path.Combine(_pneumaticFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb");
_remoteEpisode.Release.Title = illegalTitle;
Subject.DownloadNzb(_remoteEpisode);
Subject.Download(_remoteEpisode);
Mocker.GetMock<IHttpProvider>().Verify(c => c.DownloadFile(It.IsAny<string>(), expectedFilename), Times.Once());
}

View File

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
@ -12,30 +13,20 @@ using NzbDrone.Core.Download.Clients.Sabnzbd.Responses;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
{
[TestFixture]
public class SabnzbdFixture : CoreTest<Sabnzbd>
public class SabnzbdFixture : DownloadClientFixtureBase<Sabnzbd>
{
private const string URL = "http://www.nzbclub.com/nzb_download.aspx?mid=1950232";
private const string TITLE = "My Series Name - 5x2-5x3 - My title [Bluray720p] [Proper]";
private RemoteEpisode _remoteEpisode;
private SabnzbdQueue _queued;
private SabnzbdHistory _failed;
private SabnzbdHistory _completed;
[SetUp]
public void Setup()
{
_remoteEpisode = new RemoteEpisode();
_remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Release.Title = TITLE;
_remoteEpisode.Release.DownloadUrl = URL;
_remoteEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT))
.Build()
.ToList();
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new SabnzbdSettings
{
@ -47,19 +38,248 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
TvCategory = "tv",
RecentTvPriority = (int)SabnzbdPriority.High
};
_queued = new SabnzbdQueue
{
Paused = false,
Items = new List<SabnzbdQueueItem>()
{
new SabnzbdQueueItem
{
Status = SabnzbdDownloadStatus.Downloading,
Size = 1000,
Sizeleft = 10,
Timeleft = TimeSpan.FromSeconds(10),
Category = "tv",
Id = "sabnzbd_nzb12345",
Title = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE"
}
}
};
_failed = new SabnzbdHistory
{
Items = new List<SabnzbdHistoryItem>()
{
new SabnzbdHistoryItem
{
Status = SabnzbdDownloadStatus.Failed,
Size = 1000,
Category = "tv",
Id = "sabnzbd_nzb12345",
Title = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE"
}
}
};
_completed = new SabnzbdHistory
{
Items = new List<SabnzbdHistoryItem>()
{
new SabnzbdHistoryItem
{
Status = SabnzbdDownloadStatus.Completed,
Size = 1000,
Category = "tv",
Id = "sabnzbd_nzb12345",
Title = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE",
Storage = "somedirectory"
}
}
};
}
protected void WithFailedDownload()
{
Mocker.GetMock<ISabnzbdProxy>()
.Setup(s => s.DownloadNzb(It.IsAny<Stream>(), It.IsAny<String>(), It.IsAny<String>(), It.IsAny<int>(), It.IsAny<SabnzbdSettings>()))
.Returns((SabnzbdAddResponse)null);
}
protected void WithSuccessfulDownload()
{
Mocker.GetMock<ISabnzbdProxy>()
.Setup(s => s.DownloadNzb(It.IsAny<Stream>(), It.IsAny<String>(), It.IsAny<String>(), It.IsAny<int>(), It.IsAny<SabnzbdSettings>()))
.Returns(new SabnzbdAddResponse()
{
Status = true,
Ids = new List<string> { "sabznbd_nzo12345" }
});
}
protected virtual void WithQueue(SabnzbdQueue queue)
{
if (queue == null)
{
queue = new SabnzbdQueue() { Items = new List<SabnzbdQueueItem>() };
}
Mocker.GetMock<ISabnzbdProxy>()
.Setup(s => s.GetQueue(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<SabnzbdSettings>()))
.Returns(queue);
}
protected virtual void WithHistory(SabnzbdHistory history)
{
if (history == null)
history = new SabnzbdHistory() { Items = new List<SabnzbdHistoryItem>() };
Mocker.GetMock<ISabnzbdProxy>()
.Setup(s => s.GetHistory(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<SabnzbdSettings>()))
.Returns(history);
}
[Test]
public void downloadNzb_should_use_sabRecentTvPriority_when_recentEpisode_is_true()
public void GetItems_should_return_no_items_when_queue_is_empty()
{
WithQueue(null);
WithHistory(null);
Subject.GetItems().Should().BeEmpty();
}
[TestCase(SabnzbdDownloadStatus.Grabbing)]
[TestCase(SabnzbdDownloadStatus.Queued)]
public void queued_item_should_have_required_properties(SabnzbdDownloadStatus status)
{
_queued.Items.First().Status = status;
WithQueue(_queued);
WithHistory(null);
var result = Subject.GetItems().Single();
VerifyQueued(result);
result.RemainingTime.Should().NotBe(TimeSpan.Zero);
}
[TestCase(SabnzbdDownloadStatus.Paused)]
public void paused_item_should_have_required_properties(SabnzbdDownloadStatus status)
{
_queued.Items.First().Status = status;
WithQueue(_queued);
WithHistory(null);
var result = Subject.GetItems().Single();
VerifyPaused(result);
}
[TestCase(SabnzbdDownloadStatus.Checking)]
[TestCase(SabnzbdDownloadStatus.Downloading)]
[TestCase(SabnzbdDownloadStatus.QuickCheck)]
[TestCase(SabnzbdDownloadStatus.Verifying)]
[TestCase(SabnzbdDownloadStatus.Repairing)]
[TestCase(SabnzbdDownloadStatus.Fetching)]
[TestCase(SabnzbdDownloadStatus.Extracting)]
[TestCase(SabnzbdDownloadStatus.Moving)]
[TestCase(SabnzbdDownloadStatus.Running)]
public void downloading_item_should_have_required_properties(SabnzbdDownloadStatus status)
{
_queued.Items.First().Status = status;
WithQueue(_queued);
WithHistory(null);
var result = Subject.GetItems().Single();
VerifyDownloading(result);
result.RemainingTime.Should().NotBe(TimeSpan.Zero);
}
[Test]
public void completed_download_should_have_required_properties()
{
WithQueue(null);
WithHistory(_completed);
var result = Subject.GetItems().Single();
VerifyCompleted(result);
}
[Test]
public void failed_item_should_have_required_properties()
{
_completed.Items.First().Status = SabnzbdDownloadStatus.Failed;
WithQueue(null);
WithHistory(_completed);
var result = Subject.GetItems().Single();
VerifyFailed(result);
}
[Test]
public void Download_should_return_unique_id()
{
WithSuccessfulDownload();
var remoteEpisode = CreateRemoteEpisode();
var id = Subject.Download(remoteEpisode);
id.Should().NotBeNullOrEmpty();
}
[Test]
public void GetItems_should_ignore_downloads_from_other_categories()
{
_completed.Items.First().Category = "myowncat";
WithQueue(null);
WithHistory(_completed);
var items = Subject.GetItems();
items.Should().BeEmpty();
}
[Test]
public void Download_should_use_sabRecentTvPriority_when_recentEpisode_is_true()
{
Mocker.GetMock<ISabnzbdProxy>()
.Setup(s => s.DownloadNzb(It.IsAny<Stream>(), It.IsAny<String>(), It.IsAny<String>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>()))
.Returns(new SabnzbdAddResponse());
Subject.DownloadNzb(_remoteEpisode);
var remoteEpisode = CreateRemoteEpisode();
remoteEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT))
.Build()
.ToList();
Subject.Download(remoteEpisode);
Mocker.GetMock<ISabnzbdProxy>()
.Verify(v => v.DownloadNzb(It.IsAny<Stream>(), It.IsAny<String>(), It.IsAny<String>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>()), Times.Once());
}
[Test]
public void should_return_path_to_folder_instead_of_file()
{
_completed.Items.First().Storage = @"C:\sorted\Droned.S01E01.Pilot.1080p.WEB-DL-DRONE\Droned.S01E01_Pilot_1080p_WEB-DL-DRONE.mkv".AsOsAgnostic();
WithQueue(null);
WithHistory(_completed);
var result = Subject.GetItems().Single();
result.OutputPath.Should().Be(@"C:\sorted\Droned.S01E01.Pilot.1080p.WEB-DL-DRONE".AsOsAgnostic());
}
[Test]
public void should_not_blow_up_if_storage_is_drive_root()
{
_completed.Items.First().Storage = @"C:\".AsOsAgnostic();
WithQueue(null);
WithHistory(_completed);
var result = Subject.GetItems().Single();
result.OutputPath.Should().Be(@"C:\".AsOsAgnostic());
}
}
}

View File

@ -9,6 +9,7 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
using System.Collections.Generic;
namespace NzbDrone.Core.Test.Download
{
@ -16,12 +17,19 @@ namespace NzbDrone.Core.Test.Download
public class DownloadServiceFixture : CoreTest<DownloadService>
{
private RemoteEpisode _parseResult;
private List<IDownloadClient> _downloadClients;
[SetUp]
public void Setup()
{
_downloadClients = new List<IDownloadClient>();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(c => c.GetDownloadClient()).Returns(Mocker.GetMock<IDownloadClient>().Object);
.Setup(v => v.GetDownloadClients())
.Returns(_downloadClients);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(v => v.GetDownloadClient(It.IsAny<Indexers.DownloadProtocol>()))
.Returns<Indexers.DownloadProtocol>(v => _downloadClients.FirstOrDefault(d => d.Protocol == v));
var episodes = Builder<Episode>.CreateListOfSize(2)
.TheFirst(1).With(s => s.Id = 12)
@ -29,31 +37,43 @@ namespace NzbDrone.Core.Test.Download
.All().With(s => s.SeriesId = 5)
.Build().ToList();
var releaseInfo = Builder<ReleaseInfo>.CreateNew()
.With(v => v.DownloadProtocol = Indexers.DownloadProtocol.Usenet)
.Build();
_parseResult = Builder<RemoteEpisode>.CreateNew()
.With(c => c.Series = Builder<Series>.CreateNew().Build())
.With(c => c.Release = Builder<ReleaseInfo>.CreateNew().Build())
.With(c => c.Release = releaseInfo)
.With(c => c.Episodes = episodes)
.Build();
}
private void WithSuccessfulAdd()
private Mock<IDownloadClient> WithUsenetClient()
{
Mocker.GetMock<IDownloadClient>()
.Setup(s => s.DownloadNzb(It.IsAny<RemoteEpisode>()));
var mock = new Mock<IDownloadClient>(Moq.MockBehavior.Default);
_downloadClients.Add(mock.Object);
mock.SetupGet(v => v.Protocol).Returns(Indexers.DownloadProtocol.Usenet);
return mock;
}
private void WithFailedAdd()
private Mock<IDownloadClient> WithTorrentClient()
{
Mocker.GetMock<IDownloadClient>()
.Setup(s => s.DownloadNzb(It.IsAny<RemoteEpisode>()))
.Throws(new WebException());
var mock = new Mock<IDownloadClient>(Moq.MockBehavior.Default);
_downloadClients.Add(mock.Object);
mock.SetupGet(v => v.Protocol).Returns(Indexers.DownloadProtocol.Torrent);
return mock;
}
[Test]
public void Download_report_should_publish_on_grab_event()
{
WithSuccessfulAdd();
var mock = WithUsenetClient();
mock.Setup(s => s.Download(It.IsAny<RemoteEpisode>()));
Subject.DownloadReport(_parseResult);
VerifyEventPublished<EpisodeGrabbedEvent>();
@ -62,18 +82,20 @@ namespace NzbDrone.Core.Test.Download
[Test]
public void Download_report_should_grab_using_client()
{
WithSuccessfulAdd();
var mock = WithUsenetClient();
mock.Setup(s => s.Download(It.IsAny<RemoteEpisode>()));
Subject.DownloadReport(_parseResult);
Mocker.GetMock<IDownloadClient>()
.Verify(s => s.DownloadNzb(It.IsAny<RemoteEpisode>()), Times.Once());
mock.Verify(s => s.Download(It.IsAny<RemoteEpisode>()), Times.Once());
}
[Test]
public void Download_report_should_not_publish_on_failed_grab_event()
{
WithFailedAdd();
var mock = WithUsenetClient();
mock.Setup(s => s.Download(It.IsAny<RemoteEpisode>()))
.Throws(new WebException());
Assert.Throws<WebException>(() => Subject.DownloadReport(_parseResult));
@ -83,15 +105,38 @@ namespace NzbDrone.Core.Test.Download
[Test]
public void should_not_attempt_download_if_client_isnt_configure()
{
Mocker.GetMock<IProvideDownloadClient>()
.Setup(c => c.GetDownloadClient()).Returns((IDownloadClient)null);
Subject.DownloadReport(_parseResult);
Mocker.GetMock<IDownloadClient>().Verify(c => c.DownloadNzb(It.IsAny<RemoteEpisode>()), Times.Never());
Mocker.GetMock<IDownloadClient>().Verify(c => c.Download(It.IsAny<RemoteEpisode>()), Times.Never());
VerifyEventNotPublished<EpisodeGrabbedEvent>();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_send_download_to_correct_usenet_client()
{
var mockTorrent = WithTorrentClient();
var mockUsenet = WithUsenetClient();
Subject.DownloadReport(_parseResult);
mockTorrent.Verify(c => c.Download(It.IsAny<RemoteEpisode>()), Times.Never());
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteEpisode>()), Times.Once());
}
[Test]
public void should_send_download_to_correct_torrent_client()
{
var mockTorrent = WithTorrentClient();
var mockUsenet = WithUsenetClient();
_parseResult.Release.DownloadProtocol = Indexers.DownloadProtocol.Torrent;
Subject.DownloadReport(_parseResult);
mockTorrent.Verify(c => c.Download(It.IsAny<RemoteEpisode>()), Times.Once());
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteEpisode>()), Times.Never());
}
}
}

View File

@ -13,32 +13,43 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
[TestFixture]
public class FailedDownloadServiceFixture : CoreTest<FailedDownloadService>
public class FailedDownloadServiceFixture : CoreTest<DownloadTrackingService>
{
private List<HistoryItem> _completed;
private List<HistoryItem> _failed;
private List<DownloadClientItem> _completed;
private List<DownloadClientItem> _failed;
[SetUp]
public void Setup()
{
_completed = Builder<HistoryItem>.CreateListOfSize(5)
_completed = Builder<DownloadClientItem>.CreateListOfSize(5)
.All()
.With(h => h.Status = HistoryStatus.Completed)
.With(h => h.Status = DownloadItemStatus.Completed)
.Build()
.ToList();
_failed = Builder<HistoryItem>.CreateListOfSize(1)
_failed = Builder<DownloadClientItem>.CreateListOfSize(1)
.All()
.With(h => h.Status = HistoryStatus.Failed)
.With(h => h.Status = DownloadItemStatus.Failed)
.Build()
.ToList();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(c => c.GetDownloadClient()).Returns(Mocker.GetMock<IDownloadClient>().Object);
.Setup(c => c.GetDownloadClients())
.Returns( new IDownloadClient[] { Mocker.GetMock<IDownloadClient>().Object });
Mocker.GetMock<IDownloadClient>()
.SetupGet(c => c.Definition)
.Returns(new Core.Download.DownloadClientDefinition { Id = 1, Name = "testClient" });
Mocker.GetMock<IConfigService>()
.SetupGet(s => s.EnableFailedDownloadHandling)
.Returns(true);
Mocker.GetMock<IHistoryService>()
.Setup(s => s.Imported())
.Returns(new List<History.History>());
Mocker.SetConstant<IFailedDownloadService>(Mocker.Resolve<FailedDownloadService>());
}
private void GivenNoGrabbedHistory()
@ -72,7 +83,7 @@ namespace NzbDrone.Core.Test.Download
private void GivenFailedDownloadClientHistory()
{
Mocker.GetMock<IDownloadClient>()
.Setup(s => s.GetHistory(0, 20))
.Setup(s => s.GetItems())
.Returns(_failed);
}
@ -102,10 +113,10 @@ namespace NzbDrone.Core.Test.Download
public void should_not_process_if_no_download_client_history()
{
Mocker.GetMock<IDownloadClient>()
.Setup(s => s.GetHistory(0, 20))
.Returns(new List<HistoryItem>());
.Setup(s => s.GetItems())
.Returns(new List<DownloadClientItem>());
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
Mocker.GetMock<IHistoryService>()
.Verify(s => s.BetweenDates(It.IsAny<DateTime>(), It.IsAny<DateTime>(), HistoryEventType.Grabbed),
@ -117,11 +128,14 @@ namespace NzbDrone.Core.Test.Download
[Test]
public void should_not_process_if_no_failed_items_in_download_client_history()
{
GivenNoGrabbedHistory();
GivenNoFailedHistory();
Mocker.GetMock<IDownloadClient>()
.Setup(s => s.GetHistory(0, 20))
.Setup(s => s.GetItems())
.Returns(_completed);
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
Mocker.GetMock<IHistoryService>()
.Verify(s => s.BetweenDates(It.IsAny<DateTime>(), It.IsAny<DateTime>(), HistoryEventType.Grabbed),
@ -136,7 +150,7 @@ namespace NzbDrone.Core.Test.Download
GivenNoGrabbedHistory();
GivenFailedDownloadClientHistory();
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoFailedDownloads();
}
@ -156,7 +170,7 @@ namespace NzbDrone.Core.Test.Download
GivenGrabbedHistory(historyGrabbed);
GivenNoFailedHistory();
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoFailedDownloads();
}
@ -171,7 +185,7 @@ namespace NzbDrone.Core.Test.Download
.ToList();
historyGrabbed.First().Data.Add("downloadClient", "SabnzbdClient");
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().Id);
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().DownloadClientId);
GivenGrabbedHistory(historyGrabbed);
@ -184,7 +198,7 @@ namespace NzbDrone.Core.Test.Download
GivenFailedHistory(historyFailed);
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyFailedDownloads();
}
@ -202,9 +216,9 @@ namespace NzbDrone.Core.Test.Download
GivenFailedHistory(history);
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _failed.First().Id);
history.First().Data.Add("downloadClientId", _failed.First().DownloadClientId);
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoFailedDownloads();
}
@ -222,9 +236,9 @@ namespace NzbDrone.Core.Test.Download
GivenNoFailedHistory();
history.First().Data.Add("downloadClient", "SabnzbdClient");
history.First().Data.Add("downloadClientId", _failed.First().Id);
history.First().Data.Add("downloadClientId", _failed.First().DownloadClientId);
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyFailedDownloads();
}
@ -244,10 +258,10 @@ namespace NzbDrone.Core.Test.Download
history.ForEach(h =>
{
h.Data.Add("downloadClient", "SabnzbdClient");
h.Data.Add("downloadClientId", _failed.First().Id);
h.Data.Add("downloadClientId", _failed.First().DownloadClientId);
});
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyFailedDownloads(2);
}
@ -259,7 +273,7 @@ namespace NzbDrone.Core.Test.Download
.SetupGet(s => s.EnableFailedDownloadHandling)
.Returns(false);
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoFailedDownloads();
}
@ -276,7 +290,7 @@ namespace NzbDrone.Core.Test.Download
_failed.First().Message = "Unpacking failed, write error or disk is full?";
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoFailedDownloads();
}
@ -291,12 +305,12 @@ namespace NzbDrone.Core.Test.Download
.ToList();
historyGrabbed.First().Data.Add("downloadClient", "SabnzbdClient");
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().Id);
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().DownloadClientId);
GivenGrabbedHistory(historyGrabbed);
GivenNoFailedHistory();
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyFailedDownloads();
}
@ -311,13 +325,13 @@ namespace NzbDrone.Core.Test.Download
.ToList();
historyGrabbed.First().Data.Add("downloadClient", "SabnzbdClient");
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().Id);
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().DownloadClientId);
historyGrabbed.First().Data.Add("ageHours", "48");
GivenGrabbedHistory(historyGrabbed);
GivenNoFailedHistory();
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyFailedDownloads();
}
@ -332,14 +346,14 @@ namespace NzbDrone.Core.Test.Download
.ToList();
historyGrabbed.First().Data.Add("downloadClient", "SabnzbdClient");
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().Id);
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().DownloadClientId);
historyGrabbed.First().Data.Add("ageHours", "48");
GivenGrabbedHistory(historyGrabbed);
GivenNoFailedHistory();
GivenGracePeriod(6);
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyFailedDownloads();
}
@ -354,7 +368,7 @@ namespace NzbDrone.Core.Test.Download
.ToList();
historyGrabbed.First().Data.Add("downloadClient", "SabnzbdClient");
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().Id);
historyGrabbed.First().Data.Add("downloadClientId", _failed.First().DownloadClientId);
historyGrabbed.First().Data.Add("ageHours", "1");
GivenGrabbedHistory(historyGrabbed);
@ -362,7 +376,7 @@ namespace NzbDrone.Core.Test.Download
GivenGracePeriod(6);
GivenRetryLimit(1);
Subject.Execute(new CheckForFailedDownloadCommand());
Subject.Execute(new CheckForFinishedDownloadCommand());
VerifyNoFailedDownloads();
}

View File

@ -0,0 +1,54 @@
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.HealthCheck.Checks
{
[TestFixture]
public class AppDataLocationFixture : CoreTest<AppDataLocationCheck>
{
[Test]
public void should_return_warning_when_app_data_is_child_of_startup_folder()
{
Mocker.GetMock<IAppFolderInfo>()
.Setup(s => s.StartUpFolder)
.Returns(@"C:\NzbDrone".AsOsAgnostic());
Mocker.GetMock<IAppFolderInfo>()
.Setup(s => s.AppDataFolder)
.Returns(@"C:\NzbDrone\AppData".AsOsAgnostic());
Subject.Check().ShouldBeWarning();
}
[Test]
public void should_return_warning_when_app_data_is_same_as_startup_folder()
{
Mocker.GetMock<IAppFolderInfo>()
.Setup(s => s.StartUpFolder)
.Returns(@"C:\NzbDrone".AsOsAgnostic());
Mocker.GetMock<IAppFolderInfo>()
.Setup(s => s.AppDataFolder)
.Returns(@"C:\NzbDrone".AsOsAgnostic());
Subject.Check().ShouldBeWarning();
}
[Test]
public void should_return_ok_when_no_conflict()
{
Mocker.GetMock<IAppFolderInfo>()
.Setup(s => s.StartUpFolder)
.Returns(@"C:\NzbDrone".AsOsAgnostic());
Mocker.GetMock<IAppFolderInfo>()
.Setup(s => s.AppDataFolder)
.Returns(@"C:\ProgramData\NzbDrone".AsOsAgnostic());
Subject.Check().ShouldBeOk();
}
}
}

View File

@ -15,8 +15,8 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_warning_when_download_client_has_not_been_configured()
{
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClient())
.Returns((IDownloadClient)null);
.Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[0]);
Subject.Check().ShouldBeWarning();
}
@ -26,12 +26,12 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
{
var downloadClient = Mocker.GetMock<IDownloadClient>();
downloadClient.Setup(s => s.GetQueue())
downloadClient.Setup(s => s.GetItems())
.Throws<Exception>();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClient())
.Returns(downloadClient.Object);
.Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeError();
}
@ -41,12 +41,12 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
{
var downloadClient = Mocker.GetMock<IDownloadClient>();
downloadClient.Setup(s => s.GetQueue())
.Returns(new List<QueueItem>());
downloadClient.Setup(s => s.GetItems())
.Returns(new List<DownloadClientItem>());
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClient())
.Returns(downloadClient.Object);
.Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeOk();
}

View File

@ -25,17 +25,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Setup(s => s.FolderExists(DRONE_FACTORY_FOLDER))
.Returns(exists);
}
[Test]
public void should_return_warning_when_drone_factory_folder_is_not_configured()
{
Mocker.GetMock<IConfigService>()
.SetupGet(s => s.DownloadedEpisodesFolder)
.Returns("");
Subject.Check().ShouldBeWarning();
}
[Test]
public void should_return_error_when_drone_factory_folder_does_not_exist()
{

View File

@ -0,0 +1,95 @@
using System;
using System.Linq;
using System.Collections.Generic;
using FluentAssertions;
using Moq;
using FizzWare.NBuilder;
using NUnit.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Download;
namespace NzbDrone.Core.Test.HealthCheck.Checks
{
[TestFixture]
public class ImportMechanismCheckFixture : CoreTest<ImportMechanismCheck>
{
private const string DRONE_FACTORY_FOLDER = @"C:\Test\Unsorted";
private IList<TrackedDownload> _completed;
private void GivenCompletedDownloadHandling(bool? enabled = null)
{
if (enabled.HasValue)
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.IsDefined("EnableCompletedDownloadHandling"))
.Returns(true);
Mocker.GetMock<IConfigService>()
.SetupGet(s => s.EnableCompletedDownloadHandling)
.Returns(enabled.Value);
}
_completed = Builder<TrackedDownload>.CreateListOfSize(1)
.All()
.With(v => v.State == TrackedDownloadState.Downloading)
.With(v => v.DownloadItem = new DownloadClientItem())
.With(v => v.DownloadItem.Status = DownloadItemStatus.Completed)
.With(v => v.DownloadItem.OutputPath = @"C:\Test\DropFolder\myfile.mkv".AsOsAgnostic())
.Build();
Mocker.GetMock<IDownloadTrackingService>()
.Setup(v => v.GetCompletedDownloads())
.Returns(_completed.ToArray());
}
private void GivenDroneFactoryFolder(bool exists = false)
{
Mocker.GetMock<IConfigService>()
.SetupGet(s => s.DownloadedEpisodesFolder)
.Returns(DRONE_FACTORY_FOLDER.AsOsAgnostic());
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(DRONE_FACTORY_FOLDER.AsOsAgnostic()))
.Returns(exists);
}
[Test]
public void should_return_warning_when_completed_download_handling_not_configured()
{
Subject.Check().ShouldBeWarning();
}
[Test]
public void should_return_warning_when_both_completeddownloadhandling_and_dronefactory_are_not_configured()
{
GivenCompletedDownloadHandling(false);
Subject.Check().ShouldBeWarning();
}
[Test]
public void should_return_warning_when_downloadclient_drops_in_dronefactory_folder()
{
GivenCompletedDownloadHandling(true);
GivenDroneFactoryFolder(true);
_completed.First().DownloadItem.OutputPath = (DRONE_FACTORY_FOLDER + @"\myfile.mkv").AsOsAgnostic();
Subject.Check().ShouldBeWarning();
}
[Test]
public void should_return_ok_when_no_issues_found()
{
GivenCompletedDownloadHandling(true);
GivenDroneFactoryFolder(true);
Subject.Check().ShouldBeOk();
}
}
}

View File

@ -1,10 +1,9 @@
using System;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.HealthCheck;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Test.Framework;
@ -28,5 +27,25 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Subject.Check().ShouldBeError();
}
[Test]
public void should_return_error_when_app_folder_is_write_protected_and_update_automatically_is_enabled()
{
MonoOnly();
Mocker.GetMock<IConfigFileProvider>()
.Setup(s => s.UpdateAutomatically)
.Returns(true);
Mocker.GetMock<IAppFolderInfo>()
.Setup(s => s.StartUpFolder)
.Returns(@"/opt/nzbdrone");
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.WriteAllText(It.IsAny<String>(), It.IsAny<String>()))
.Throws<Exception>();
Subject.Check().ShouldBeError();
}
}
}

View File

@ -29,37 +29,6 @@ namespace NzbDrone.Core.Test.IndexerTests
Mocker.SetConstant<IEnumerable<IIndexer>>(_indexers);
}
[Test]
public void should_create_default_indexer_on_startup()
{
IList<IndexerDefinition> storedIndexers = null;
Mocker.GetMock<IIndexerRepository>()
.Setup(c => c.InsertMany(It.IsAny<IList<IndexerDefinition>>()))
.Callback<IList<IndexerDefinition>>(indexers => storedIndexers = indexers);
Subject.Handle(new ApplicationStartedEvent());
storedIndexers.Should().NotBeEmpty();
storedIndexers.Select(c => c.Name).Should().OnlyHaveUniqueItems();
storedIndexers.Select(c => c.Enable).Should().NotBeEmpty();
storedIndexers.Select(c => c.Implementation).Should().NotContainNulls();
}
[Test]
public void getting_list_of_indexers()
{
Mocker.SetConstant<IIndexerRepository>(Mocker.Resolve<IndexerRepository>());
Subject.Handle(new ApplicationStartedEvent());
var indexers = Subject.All().ToList();
indexers.Should().NotBeEmpty();
indexers.Should().NotContain(c => c.Settings == null);
indexers.Should().NotContain(c => c.Name == null);
indexers.Select(c => c.Name).Should().OnlyHaveUniqueItems();
}
[Test]
public void should_remove_missing_indexers_on_startup()
{

View File

@ -1,7 +1,6 @@
using System.Collections.Generic;
using FluentAssertions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Eztv;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Indexers.Wombles;
using NzbDrone.Core.Parser.Model;
@ -37,39 +36,6 @@ namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests
ValidateResult(result, skipSize: true, skipInfo: true);
}
[Test]
public void extv_rss()
{
var indexer = new Eztv();
indexer.Definition = new IndexerDefinition
{
Name = "Eztv",
Settings = NullConfig.Instance
};
var result = Subject.FetchRss(indexer);
ValidateTorrentResult(result, skipSize: false, skipInfo: true);
}
[Test]
public void nzbsorg_rss()
{
var indexer = new Newznab();
indexer.Definition = new IndexerDefinition();
indexer.Definition.Name = "nzbs.org";
indexer.Definition.Settings = new NewznabSettings
{
ApiKey = "64d61d3cfd4b75e51d01cbc7c6a78275",
Url = "http://nzbs.org"
};
var result = Subject.FetchRss(indexer);
ValidateResult(result);
}
private void ValidateResult(IList<ReleaseInfo> reports, bool skipSize = false, bool skipInfo = false)
{

View File

@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.IndexerTests
indexer.Setup(s => s.GetSeasonSearchUrls(It.IsAny<String>(), It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>()))
.Returns(new List<string> { "http://www.nzbdrone.com" });
indexer.SetupGet(s => s.SupportsPaging).Returns(paging);
indexer.SetupGet(s => s.SupportedPageSize).Returns(paging ? 100 : 0);
var definition = new IndexerDefinition();
definition.Name = "Test";

View File

@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.MediaFiles
.Returns("c:\\drop\\".AsOsAgnostic());
Mocker.GetMock<IImportApprovedEpisodes>()
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true))
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null))
.Returns(new List<ImportDecision>());
}
@ -77,6 +77,8 @@ namespace NzbDrone.Core.Test.MediaFiles
[Test]
public void should_skip_if_file_is_in_use_by_another_process()
{
GivenValidSeries();
Mocker.GetMock<IDiskProvider>().Setup(c => c.IsFileLocked(It.IsAny<string>()))
.Returns(true);
@ -122,7 +124,7 @@ namespace NzbDrone.Core.Test.MediaFiles
public void should_not_delete_folder_if_no_files_were_imported()
{
Mocker.GetMock<IImportApprovedEpisodes>()
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), false))
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), false, null))
.Returns(new List<ImportDecision>());
Subject.Execute(new DownloadedEpisodesScanCommand());
@ -132,7 +134,7 @@ namespace NzbDrone.Core.Test.MediaFiles
}
[Test]
public void should_delete_folder_if_files_were_imported_and_video_files_remain()
public void should_not_delete_folder_if_files_were_imported_and_video_files_remain()
{
GivenValidSeries();
@ -146,7 +148,7 @@ namespace NzbDrone.Core.Test.MediaFiles
.Returns(imported);
Mocker.GetMock<IImportApprovedEpisodes>()
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true))
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null))
.Returns(imported);
Subject.Execute(new DownloadedEpisodesScanCommand());
@ -172,7 +174,7 @@ namespace NzbDrone.Core.Test.MediaFiles
.Returns(imported);
Mocker.GetMock<IImportApprovedEpisodes>()
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true))
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null))
.Returns(imported);
Mocker.GetMock<ISampleService>()
@ -211,13 +213,13 @@ namespace NzbDrone.Core.Test.MediaFiles
private void VerifyNoImport()
{
Mocker.GetMock<IImportApprovedEpisodes>().Verify(c => c.Import(It.IsAny<List<ImportDecision>>(), true),
Mocker.GetMock<IImportApprovedEpisodes>().Verify(c => c.Import(It.IsAny<List<ImportDecision>>(), true, null),
Times.Never());
}
private void VerifyImport()
{
Mocker.GetMock<IImportApprovedEpisodes>().Verify(c => c.Import(It.IsAny<List<ImportDecision>>(), true),
Mocker.GetMock<IImportApprovedEpisodes>().Verify(c => c.Import(It.IsAny<List<ImportDecision>>(), true, null),
Times.Once());
}
}

View File

@ -1,75 +0,0 @@
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
[TestFixture]
public class NotInUseSpecificationFixture : CoreTest<NotInUseSpecification>
{
private LocalEpisode _localEpisode;
[SetUp]
public void Setup()
{
_localEpisode = new LocalEpisode
{
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi".AsOsAgnostic(),
Size = 100,
Series = Builder<Series>.CreateNew().Build()
};
}
private void GivenChildOfSeries()
{
_localEpisode.ExistingFile = true;
}
[Test]
public void should_return_true_if_file_is_under_series_folder()
{
GivenChildOfSeries();
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
}
[Test]
public void should_not_check_for_file_in_use_if_child_of_series_folder()
{
GivenChildOfSeries();
Subject.IsSatisfiedBy(_localEpisode);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.IsFileLocked(It.IsAny<string>()), Times.Never());
}
[Test]
public void should_return_false_if_file_is_in_use()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.IsFileLocked(It.IsAny<string>()))
.Returns(true);
Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse();
}
[Test]
public void should_return_true_if_file_is_not_in_use()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.IsFileLocked(It.IsAny<string>()))
.Returns(false);
Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue();
}
}
}

View File

@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
private void GivenInWorkingFolder()
{
_localEpisode.Path = @"C:\Test\Unsorted TV\_UNPACK_30.rock\30.rock.s01e01.avi".AsOsAgnostic();
_localEpisode.Path = @"C:\Test\Unsorted TV\_UNPACK_30.rock\someSubFolder\30.rock.s01e01.avi".AsOsAgnostic();
}
private void GivenLastWriteTimeUtc(DateTime time)

View File

@ -57,20 +57,20 @@ namespace NzbDrone.Core.Test.MediaFiles
}
Mocker.GetMock<IUpgradeMediaFiles>()
.Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>()))
.Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>(), false))
.Returns(new EpisodeFileMoveResult());
}
[Test]
public void should_return_empty_list_if_there_are_no_approved_decisions()
{
Subject.Import(_rejectedDecisions).Should().BeEmpty();
Subject.Import(_rejectedDecisions, false).Should().BeEmpty();
}
[Test]
public void should_import_each_approved()
{
Subject.Import(_approvedDecisions).Should().HaveCount(5);
Subject.Import(_approvedDecisions, false).Should().HaveCount(5);
}
[Test]
@ -80,7 +80,7 @@ namespace NzbDrone.Core.Test.MediaFiles
all.AddRange(_rejectedDecisions);
all.AddRange(_approvedDecisions);
Subject.Import(all).Should().HaveCount(5);
Subject.Import(all, false).Should().HaveCount(5);
}
[Test]
@ -90,7 +90,7 @@ namespace NzbDrone.Core.Test.MediaFiles
all.AddRange(_approvedDecisions);
all.Add(new ImportDecision(_approvedDecisions.First().LocalEpisode));
Subject.Import(all).Should().HaveCount(5);
Subject.Import(all, false).Should().HaveCount(5);
}
[Test]
@ -99,7 +99,7 @@ namespace NzbDrone.Core.Test.MediaFiles
Subject.Import(new List<ImportDecision> {_approvedDecisions.First()}, true);
Mocker.GetMock<IUpgradeMediaFiles>()
.Verify(v => v.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), _approvedDecisions.First().LocalEpisode),
.Verify(v => v.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), _approvedDecisions.First().LocalEpisode, false),
Times.Once());
}
@ -115,10 +115,10 @@ namespace NzbDrone.Core.Test.MediaFiles
[Test]
public void should_not_move_existing_files()
{
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() });
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, false);
Mocker.GetMock<IUpgradeMediaFiles>()
.Verify(v => v.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), _approvedDecisions.First().LocalEpisode),
.Verify(v => v.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), _approvedDecisions.First().LocalEpisode, false),
Times.Never());
}
@ -143,7 +143,7 @@ namespace NzbDrone.Core.Test.MediaFiles
all.Add(fileDecision);
all.Add(sampleDecision);
var results = Subject.Import(all);
var results = Subject.Import(all, false);
results.Should().HaveCount(1);
results.Should().ContainSingle(d => d.LocalEpisode.Size == fileDecision.LocalEpisode.Size);

View File

@ -0,0 +1,78 @@
using System.IO;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Metadata;
using NzbDrone.Core.Metadata.Consumers.Roksbox;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Metadata.Consumers.Roksbox
{
[TestFixture]
public class FindMetadataFileFixture : CoreTest<RoksboxMetadata>
{
private Series _series;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\The.Series".AsOsAgnostic())
.Build();
}
[Test]
public void should_return_null_if_filename_is_not_handled()
{
var path = Path.Combine(_series.Path, "file.jpg");
Subject.FindMetadataFile(_series, path).Should().BeNull();
}
[TestCase("Specials")]
[TestCase("specials")]
[TestCase("Season 1")]
public void should_return_season_image(string folder)
{
var path = Path.Combine(_series.Path, folder, folder + ".jpg");
Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.SeasonImage);
}
[TestCase(".xml", MetadataType.EpisodeMetadata)]
[TestCase(".jpg", MetadataType.EpisodeImage)]
public void should_return_metadata_for_episode_if_valid_file_for_episode(string extension, MetadataType type)
{
var path = Path.Combine(_series.Path, "the.series.s01e01.episode" + extension);
Subject.FindMetadataFile(_series, path).Type.Should().Be(type);
}
[TestCase(".xml")]
[TestCase(".jpg")]
public void should_return_null_if_not_valid_file_for_episode(string extension)
{
var path = Path.Combine(_series.Path, "the.series.episode" + extension);
Subject.FindMetadataFile(_series, path).Should().BeNull();
}
[Test]
public void should_not_return_metadata_if_image_file_is_a_thumb()
{
var path = Path.Combine(_series.Path, "the.series.s01e01.episode-thumb.jpg");
Subject.FindMetadataFile(_series, path).Should().BeNull();
}
[Test]
public void should_return_series_image_for_folder_jpg_in_series_folder()
{
var path = Path.Combine(_series.Path, new DirectoryInfo(_series.Path).Name + ".jpg");
Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.SeriesImage);
}
}
}

View File

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Metadata;
using NzbDrone.Core.Metadata.Consumers.Wdtv;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Metadata.Consumers.Wdtv
{
[TestFixture]
public class FindMetadataFileFixture : CoreTest<WdtvMetadata>
{
private Series _series;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\The.Series".AsOsAgnostic())
.Build();
}
[Test]
public void should_return_null_if_filename_is_not_handled()
{
var path = Path.Combine(_series.Path, "file.jpg");
Subject.FindMetadataFile(_series, path).Should().BeNull();
}
[TestCase("Specials")]
[TestCase("specials")]
[TestCase("Season 1")]
public void should_return_season_image(string folder)
{
var path = Path.Combine(_series.Path, folder, "folder.jpg");
Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.SeasonImage);
}
[TestCase(".xml", MetadataType.EpisodeMetadata)]
[TestCase(".metathumb", MetadataType.EpisodeImage)]
public void should_return_metadata_for_episode_if_valid_file_for_episode(string extension, MetadataType type)
{
var path = Path.Combine(_series.Path, "the.series.s01e01.episode" + extension);
Subject.FindMetadataFile(_series, path).Type.Should().Be(type);
}
[TestCase(".xml")]
[TestCase(".metathumb")]
public void should_return_null_if_not_valid_file_for_episode(string extension)
{
var path = Path.Combine(_series.Path, "the.series.episode" + extension);
Subject.FindMetadataFile(_series, path).Should().BeNull();
}
[Test]
public void should_return_series_image_for_folder_jpg_in_series_folder()
{
var path = Path.Combine(_series.Path, "folder.jpg");
Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.SeriesImage);
}
}
}

View File

@ -115,18 +115,21 @@
<Compile Include="DecisionEngineTests\Search\SeriesSpecificationFixture.cs" />
<Compile Include="Download\DownloadApprovedReportsTests\DownloadApprovedFixture.cs" />
<Compile Include="Download\DownloadApprovedReportsTests\GetQualifiedReportsFixture.cs" />
<Compile Include="Download\DownloadClientTests\BlackholeProviderFixture.cs" />
<Compile Include="Download\DownloadClientTests\NzbgetTests\DownloadNzbFixture.cs" />
<Compile Include="Download\DownloadClientTests\NzbgetTests\QueueFixture.cs" />
<Compile Include="Download\DownloadClientTests\Blackhole\UsenetBlackholeFixture.cs" />
<Compile Include="Download\DownloadClientTests\DownloadClientFixtureBase.cs" />
<Compile Include="Download\DownloadClientTests\NzbgetTests\NzbgetFixture.cs" />
<Compile Include="Download\DownloadClientTests\PneumaticProviderFixture.cs" />
<Compile Include="Download\DownloadClientTests\SabnzbdTests\SabnzbdFixture.cs" />
<Compile Include="Download\DownloadServiceFixture.cs" />
<Compile Include="Download\CompletedDownloadServiceFixture.cs" />
<Compile Include="Download\FailedDownloadServiceFixture.cs" />
<Compile Include="Framework\CoreTest.cs" />
<Compile Include="Framework\DbTest.cs" />
<Compile Include="Framework\NBuilderExtensions.cs" />
<Compile Include="HealthCheck\Checks\AppDataLocationFixture.cs" />
<Compile Include="HealthCheck\Checks\RootFolderCheckFixture.cs" />
<Compile Include="HealthCheck\Checks\DownloadClientCheckFixture.cs" />
<Compile Include="HealthCheck\Checks\ImportMechanismCheckFixture.cs" />
<Compile Include="HealthCheck\Checks\UpdateCheckFixture.cs" />
<Compile Include="HealthCheck\Checks\IndexerCheckFixture.cs" />
<Compile Include="HealthCheck\Checks\DroneFactoryCheckFixture.cs" />
@ -156,7 +159,6 @@
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotInUseSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\SampleServiceFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecificationFixture.cs" />
@ -172,6 +174,8 @@
<Compile Include="Messaging\Commands\CommandFixture.cs" />
<Compile Include="Messaging\Events\EventAggregatorFixture.cs" />
<Compile Include="MetadataSourceTests\TraktProxyFixture.cs" />
<Compile Include="Metadata\Consumers\Roksbox\FindMetadataFileFixture.cs" />
<Compile Include="Metadata\Consumers\Wdtv\FindMetadataFileFixture.cs" />
<Compile Include="NotificationTests\Xbmc\GetJsonVersionFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Http\ActivePlayersFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Http\CheckForErrorFixture.cs" />

View File

@ -33,13 +33,14 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[ACX]Hack Sign 01 Role Play [Kosaka] [9C57891E].mkv", "Hack Sign", 1, 0, 0)]
[TestCase("[SFW-sage] Bakuman S3 - 12 [720p][D07C91FC]", "Bakuman S3", 12, 0, 0)]
[TestCase("ducktales_e66_time_is_money_part_one_marking_time", "DuckTales", 66, 0, 0)]
[TestCase("[Underwater-FFF] No Game No Life - 01 (720p) [27AAA0A0].mkv", "No Game No Life", 1, 0, 0)]
public void should_parse_absolute_numbers(string postTitle, string title, int absoluteEpisodeNumber, int seasonNumber, int episodeNumber)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Should().NotBeNull();
result.AbsoluteEpisodeNumbers.First().Should().Be(absoluteEpisodeNumber);
result.AbsoluteEpisodeNumbers.Single().Should().Be(absoluteEpisodeNumber);
result.SeasonNumber.Should().Be(seasonNumber);
result.EpisodeNumbers.FirstOrDefault().Should().Be(episodeNumber);
result.EpisodeNumbers.SingleOrDefault().Should().Be(episodeNumber);
result.SeriesTitle.Should().Be(title.CleanSeriesTitle());
result.FullSeason.Should().BeFalse();
}

View File

@ -40,6 +40,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Shield,.The.1x13.Tueurs.De.Flics.FR.DVDRip.XviD", Language.French)]
[TestCase("True.Detective.S01E01.1080p.WEB-DL.Rus.Eng.TVKlondike", Language.Russian)]
[TestCase("The.Trip.To.Italy.S02E01.720p.HDTV.x264-TLA", Language.English)]
[TestCase("Revolution S01E03 No Quarter 2012 WEB-DL 720p Nordic-philipo mkv", Language.Norwegian)]
public void should_parse_language(string postTitle, Language language)
{
var result = Parser.Parser.ParseTitle(postTitle);

View File

@ -16,12 +16,26 @@ namespace NzbDrone.Core.Test.ParserTests
new object[] { Quality.DVD },
new object[] { Quality.WEBDL480p },
new object[] { Quality.HDTV720p },
new object[] { Quality.HDTV1080p },
new object[] { Quality.WEBDL720p },
new object[] { Quality.WEBDL1080p },
new object[] { Quality.Bluray720p },
new object[] { Quality.Bluray1080p }
};
public static object[] OtherSourceQualityParserCases =
{
new object[] { "SD TV", Quality.SDTV },
new object[] { "SD DVD", Quality.DVD },
new object[] { "480p WEB-DL", Quality.WEBDL480p },
new object[] { "HD TV", Quality.HDTV720p },
new object[] { "1080p HD TV", Quality.HDTV1080p },
new object[] { "720p WEB-DL", Quality.WEBDL720p },
new object[] { "1080p WEB-DL", Quality.WEBDL1080p },
new object[] { "720p BluRay", Quality.Bluray720p },
new object[] { "1080p BluRay", Quality.Bluray1080p }
};
[TestCase("S07E23 .avi ", false)]
[TestCase("The.Shield.S01E13.x264-CtrlSD", false)]
[TestCase("Nikita S02E01 HDTV XviD 2HD", false)]
@ -64,7 +78,8 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Elementary.S01E10.The.Leviathan.480p.WEB-DL.x264-mSD", false)]
[TestCase("Glee.S04E10.Glee.Actually.480p.WEB-DL.x264-mSD", false)]
[TestCase("The.Big.Bang.Theory.S06E11.The.Santa.Simulation.480p.WEB-DL.x264-mSD", false)]
[TestCase("The.Big.Bang.Theory.S06E11.The.Santa.Simulation.480p.WEB-DL.x264-mSD", false)]
[TestCase("Da.Vincis.Demons.S02E04.480p.WEB.DL.nSD.x264-NhaNc3", false)]
public void should_parse_webdl480p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.WEBDL480p, proper);
@ -105,6 +120,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("S07E23 - [WEBDL].mkv ", false)]
[TestCase("Fringe S04E22 720p WEB-DL DD5.1 H264-EbP.mkv", false)]
[TestCase("House.S04.720p.Web-Dl.Dd5.1.h264-P2PACK", false)]
[TestCase("Da.Vincis.Demons.S02E04.720p.WEB.DL.nSD.x264-NhaNc3", false)]
public void should_parse_webdl720p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.WEBDL720p, proper);
@ -144,6 +160,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("POI S02E11 1080i HDTV DD5.1 MPEG2-TrollHD", false)]
[TestCase("How I Met Your Mother S01E18 Nothing Good Happens After 2 A.M. 720p HDTV DD5.1 MPEG2-TrollHD", false)]
[TestCase("The Voice S01E11 The Finals 1080i HDTV DD5.1 MPEG2-TrollHD", false)]
[TestCase("Californication.S07E11.1080i.HDTV.DD5.1.MPEG2-NTb.ts", false)]
[TestCase("Game of Thrones S04E10 1080i HDTV MPEG2 DD5.1-CtrlHD.ts", false)]
[TestCase("VICE.S02E05.1080i.HDTV.DD2.0.MPEG2-NTb.ts", false)]
public void should_parse_raw_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.RAWHD, proper);
@ -164,6 +183,18 @@ namespace NzbDrone.Core.Test.ParserTests
result.Quality.Should().Be(quality);
}
[Test, TestCaseSource("OtherSourceQualityParserCases")]
public void should_parse_quality_from_other_source(string qualityString, Quality quality)
{
foreach (var c in new char[] { '-', '.', ' ', '_' })
{
var title = String.Format("My series S01E01 {0}", qualityString.Replace(' ', c));
ParseAndVerifyQuality(title, quality, false);
}
}
private void ParseAndVerifyQuality(string title, Quality quality, bool proper)
{
var result = Parser.QualityParser.ParseQuality(title);

View File

@ -4,7 +4,6 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class ReleaseGroupParserFixture : CoreTest
{
@ -19,6 +18,11 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("The Office - S01E01 - Pilot [HTDV-1080p]", "DRONE")]
[TestCase("The.Walking.Dead.S04E13.720p.WEB-DL.AAC2.0.H.264-Cyphanix", "Cyphanix")]
[TestCase("Arrow.S02E01.720p.WEB-DL.DD5.1.H.264.mkv", "DRONE")]
[TestCase("Series Title S01E01 Episode Title", "DRONE")]
[TestCase("The Colbert Report - 2014-06-02 - Thomas Piketty.mkv", "DRONE")]
[TestCase("Real Time with Bill Maher S12E17 May 23, 2014.mp4", "DRONE")]
[TestCase("Reizen Waes - S01E08 - Transistrië, Zuid-Ossetië en Abchazië SDTV.avi", "DRONE")]
[TestCase("Simpsons 10x11 - Wild Barts Cant Be Broken [rl].avi", "DRONE")]
public void should_parse_release_group(string title, string expected)
{
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);

View File

@ -49,6 +49,9 @@ namespace NzbDrone.Core.Test.UpdateTests
}
Mocker.GetMock<IAppFolderInfo>().SetupGet(c => c.TempFolder).Returns(TempFolder);
Mocker.GetMock<IAppFolderInfo>().SetupGet(c => c.StartUpFolder).Returns(@"C:\NzbDrone".AsOsAgnostic);
Mocker.GetMock<IAppFolderInfo>().SetupGet(c => c.AppDataFolder).Returns(@"C:\ProgramData\NzbDrone".AsOsAgnostic);
Mocker.GetMock<ICheckUpdateService>().Setup(c => c.AvailableUpdate()).Returns(_updatePackage);
Mocker.GetMock<IVerifyUpdates>().Setup(c => c.Verify(It.IsAny<UpdatePackage>(), It.IsAny<String>())).Returns(true);
@ -101,7 +104,6 @@ namespace NzbDrone.Core.Test.UpdateTests
Subject.Execute(new ApplicationUpdateCommand());
Mocker.GetMock<IHttpProvider>().Verify(c => c.DownloadFile(_updatePackage.Url, updateArchive));
}
@ -112,7 +114,6 @@ namespace NzbDrone.Core.Test.UpdateTests
Subject.Execute(new ApplicationUpdateCommand());
Mocker.GetMock<IArchiveService>().Verify(c => c.Extract(updateArchive, _sandboxFolder));
}
@ -239,6 +240,26 @@ namespace NzbDrone.Core.Test.UpdateTests
updateSubFolder.GetFiles().Should().NotBeEmpty();
}
[Test]
public void should_log_error_when_app_data_is_child_of_startup_folder()
{
Mocker.GetMock<IAppFolderInfo>().SetupGet(c => c.StartUpFolder).Returns(@"C:\NzbDrone".AsOsAgnostic);
Mocker.GetMock<IAppFolderInfo>().SetupGet(c => c.AppDataFolder).Returns(@"C:\NzbDrone\AppData".AsOsAgnostic);
Subject.Execute(new ApplicationUpdateCommand());
ExceptionVerification.ExpectedErrors(1);
}
[Test]
public void should_log_error_when_app_data_is_same_as_startup_folder()
{
Mocker.GetMock<IAppFolderInfo>().SetupGet(c => c.StartUpFolder).Returns(@"C:\NzbDrone".AsOsAgnostic);
Mocker.GetMock<IAppFolderInfo>().SetupGet(c => c.AppDataFolder).Returns(@"C:\NzbDrone".AsOsAgnostic);
Subject.Execute(new ApplicationUpdateCommand());
ExceptionVerification.ExpectedErrors(1);
}
[TearDown]
public void TearDown()
{

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration />

View File

@ -72,6 +72,11 @@ namespace NzbDrone.Core.Configuration
_eventAggregator.PublishEvent(new ConfigSavedEvent());
}
public Boolean IsDefined(String key)
{
return _repository.Get(key.ToLower()) != null;
}
public String DownloadedEpisodesFolder
{
get { return GetValue(ConfigKey.DownloadedEpisodesFolder.ToString()); }
@ -117,6 +122,27 @@ namespace NzbDrone.Core.Configuration
set { SetValue("AutoDownloadPropers", value); }
}
public Boolean EnableCompletedDownloadHandling
{
get { return GetValueBoolean("EnableCompletedDownloadHandling", false); }
set { SetValue("EnableCompletedDownloadHandling", value); }
}
public Boolean RemoveCompletedDownloads
{
get { return GetValueBoolean("RemoveCompletedDownloads", false); }
set { SetValue("RemoveCompletedDownloads", value); }
}
public Boolean EnableFailedDownloadHandling
{
get { return GetValueBoolean("EnableFailedDownloadHandling", true); }
set { SetValue("EnableFailedDownloadHandling", value); }
}
public Boolean AutoRedownloadFailed
{
get { return GetValueBoolean("AutoRedownloadFailed", true); }
@ -152,13 +178,6 @@ namespace NzbDrone.Core.Configuration
set { SetValue("BlacklistRetryLimit", value); }
}
public Boolean EnableFailedDownloadHandling
{
get { return GetValueBoolean("EnableFailedDownloadHandling", true); }
set { SetValue("EnableFailedDownloadHandling", value); }
}
public Boolean CreateEmptySeriesFolders
{
get { return GetValueBoolean("CreateEmptySeriesFolders", false); }
@ -186,6 +205,13 @@ namespace NzbDrone.Core.Configuration
set { SetValue("DownloadedEpisodesScanInterval", value); }
}
public Int32 DownloadClientHistoryLimit
{
get { return GetValueInt("DownloadClientHistoryLimit", 30); }
set { SetValue("DownloadClientHistoryLimit", value); }
}
public Boolean SkipFreeSpaceCheckWhenImporting
{
get { return GetValueBoolean("SkipFreeSpaceCheckWhenImporting", false); }

View File

@ -11,15 +11,21 @@ namespace NzbDrone.Core.Configuration
Dictionary<String, Object> AllWithDefaults();
void SaveConfigDictionary(Dictionary<string, object> configValues);
Boolean IsDefined(String key);
//Download Client
String DownloadedEpisodesFolder { get; set; }
String DownloadClientWorkingFolders { get; set; }
Int32 DownloadedEpisodesScanInterval { get; set; }
Int32 DownloadClientHistoryLimit { get; set; }
//Failed Download Handling (Download client)
//Completed/Failed Download Handling (Download client)
Boolean EnableCompletedDownloadHandling { get; set; }
Boolean RemoveCompletedDownloads { get; set; }
Boolean EnableFailedDownloadHandling { get; set; }
Boolean AutoRedownloadFailed { get; set; }
Boolean RemoveFailedDownloads { get; set; }
Boolean EnableFailedDownloadHandling { get; set; }
Int32 BlacklistGracePeriod { get; set; }
Int32 BlacklistRetryInterval { get; set; }
Int32 BlacklistRetryLimit { get; set; }

View File

@ -0,0 +1,243 @@
using System;
using System.Data;
using System.Linq;
using System.Collections.Generic;
using FluentMigrator;
using Newtonsoft.Json;
using NzbDrone.Common;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration.Framework;
using System.IO;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(51)]
public class download_client_import : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(EnableCompletedDownloadHandlingForNewUsers);
Execute.WithConnection(ConvertFolderSettings);
Execute.WithConnection(AssociateImportedHistoryItems);
}
private void EnableCompletedDownloadHandlingForNewUsers(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand cmd = conn.CreateCommand())
{
cmd.Transaction = tran;
cmd.CommandText = @"SELECT Value FROM Config WHERE Key = 'downloadedepisodesfolder'";
var result = cmd.ExecuteScalar();
if (result == null)
{
cmd.CommandText = @"INSERT INTO Config (Key, Value) VALUES ('enablecompleteddownloadhandling', 'True')";
cmd.ExecuteNonQuery();
}
}
}
private void ConvertFolderSettings(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand downloadClientsCmd = conn.CreateCommand())
{
downloadClientsCmd.Transaction = tran;
downloadClientsCmd.CommandText = @"SELECT Value FROM Config WHERE Key = 'downloadedepisodesfolder'";
var downloadedEpisodesFolder = downloadClientsCmd.ExecuteScalar() as String;
downloadClientsCmd.Transaction = tran;
downloadClientsCmd.CommandText = @"SELECT Id, Implementation, Settings, ConfigContract FROM DownloadClients WHERE ConfigContract = 'FolderSettings'";
using (IDataReader downloadClientReader = downloadClientsCmd.ExecuteReader())
{
while (downloadClientReader.Read())
{
var id = downloadClientReader.GetInt32(0);
var implementation = downloadClientReader.GetString(1);
var settings = downloadClientReader.GetString(2);
var configContract = downloadClientReader.GetString(3);
var settingsJson = JsonConvert.DeserializeObject(settings) as Newtonsoft.Json.Linq.JObject;
if (implementation == "Blackhole")
{
var newSettings = new
{
NzbFolder = settingsJson.Value<String>("folder"),
WatchFolder = downloadedEpisodesFolder
}.ToJson();
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE DownloadClients SET Implementation = ?, Settings = ?, ConfigContract = ? WHERE Id = ?";
updateCmd.AddParameter("UsenetBlackhole");
updateCmd.AddParameter(newSettings);
updateCmd.AddParameter("UsenetBlackholeSettings");
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
else if (implementation == "Pneumatic")
{
var newSettings = new
{
NzbFolder = settingsJson.Value<String>("folder")
}.ToJson();
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE DownloadClients SET Settings = ?, ConfigContract = ? WHERE Id = ?";
updateCmd.AddParameter(newSettings);
updateCmd.AddParameter("PneumaticSettings");
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
else
{
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "DELETE FROM DownloadClients WHERE Id = ?";
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
private sealed class MigrationHistoryItem
{
public Int32 Id { get; set; }
public Int32 EpisodeId { get; set; }
public Int32 SeriesId { get; set; }
public String SourceTitle { get; set; }
public DateTime Date { get; set; }
public Dictionary<String, String> Data { get; set; }
public MigrationHistoryEventType EventType { get; set; }
}
private enum MigrationHistoryEventType
{
Unknown = 0,
Grabbed = 1,
SeriesFolderImported = 2,
DownloadFolderImported = 3,
DownloadFailed = 4
}
private void AssociateImportedHistoryItems(IDbConnection conn, IDbTransaction tran)
{
var historyItems = new List<MigrationHistoryItem>();
using (IDbCommand historyCmd = conn.CreateCommand())
{
historyCmd.Transaction = tran;
historyCmd.CommandText = @"SELECT Id, EpisodeId, SeriesId, SourceTitle, Date, Data, EventType FROM History WHERE EventType NOT NULL";
using (IDataReader historyRead = historyCmd.ExecuteReader())
{
while (historyRead.Read())
{
historyItems.Add(new MigrationHistoryItem
{
Id = historyRead.GetInt32(0),
EpisodeId = historyRead.GetInt32(1),
SeriesId = historyRead.GetInt32(2),
SourceTitle = historyRead.GetString(3),
Date = historyRead.GetDateTime(4),
Data = Json.Deserialize<Dictionary<String, String>>(historyRead.GetString(5)),
EventType = (MigrationHistoryEventType)historyRead.GetInt32(6)
});
}
}
}
var numHistoryItemsNotAssociated = historyItems.Count(v => v.EventType == MigrationHistoryEventType.DownloadFolderImported &&
v.Data.GetValueOrDefault("downloadClientId") == null);
if (numHistoryItemsNotAssociated == 0)
{
return;
}
var historyItemsToAssociate = new Dictionary<MigrationHistoryItem, MigrationHistoryItem>();
var historyItemsLookup = historyItems.ToLookup(v => v.EpisodeId);
foreach (var historyItemGroup in historyItemsLookup)
{
var list = historyItemGroup.ToList();
for (int i = 0; i < list.Count - 1; i++)
{
var grabbedEvent = list[i];
if (grabbedEvent.EventType != MigrationHistoryEventType.Grabbed) continue;
if (grabbedEvent.Data.GetValueOrDefault("downloadClient") == null || grabbedEvent.Data.GetValueOrDefault("downloadClientId") == null) continue;
// Check if it is already associated with a failed/imported event.
int j;
for (j = i + 1; j < list.Count;j++)
{
if (list[j].EventType != MigrationHistoryEventType.DownloadFolderImported &&
list[j].EventType != MigrationHistoryEventType.DownloadFailed)
{
continue;
}
if (list[j].Data.ContainsKey("downloadClient") && list[j].Data["downloadClient"] == grabbedEvent.Data["downloadClient"] &&
list[j].Data.ContainsKey("downloadClientId") && list[j].Data["downloadClientId"] == grabbedEvent.Data["downloadClientId"])
{
break;
}
}
if (j != list.Count)
{
list.RemoveAt(j);
list.RemoveAt(i--);
continue;
}
var importedEvent = list[i + 1];
if (importedEvent.EventType != MigrationHistoryEventType.DownloadFolderImported) continue;
var droppedPath = importedEvent.Data.GetValueOrDefault("droppedPath");
if (droppedPath != null && new FileInfo(droppedPath).Directory.Name == grabbedEvent.SourceTitle)
{
historyItemsToAssociate[importedEvent] = grabbedEvent;
list.RemoveAt(i + 1);
list.RemoveAt(i--);
}
}
}
foreach (var pair in historyItemsToAssociate)
{
using (IDbCommand updateHistoryCmd = conn.CreateCommand())
{
pair.Key.Data["downloadClient"] = pair.Value.Data["downloadClient"];
pair.Key.Data["downloadClientId"] = pair.Value.Data["downloadClientId"];
updateHistoryCmd.Transaction = tran;
updateHistoryCmd.CommandText = "UPDATE History SET Data = ? WHERE Id = ?";
updateHistoryCmd.AddParameter(pair.Key.Data.ToJson());
updateHistoryCmd.AddParameter(pair.Key.Id);
updateHistoryCmd.ExecuteNonQuery();
}
}
_logger.Info("Updated old History items. {0}/{1} old ImportedEvents were associated with GrabbedEvents.", historyItemsToAssociate.Count, numHistoryItemsNotAssociated);
}
}
}

View File

@ -6,11 +6,11 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
{
public abstract class NzbDroneMigrationBase : FluentMigrator.Migration
{
private Logger _logger;
protected readonly Logger _logger;
protected NzbDroneMigrationBase()
{
_logger = NzbDroneLogger.GetLogger();
_logger = NzbDroneLogger.GetLogger(this);
}
protected virtual void MainDbUpgrade()

View File

@ -37,7 +37,8 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<Config>().RegisterModel("Config");
Mapper.Entity<RootFolder>().RegisterModel("RootFolders").Ignore(r => r.FreeSpace);
Mapper.Entity<IndexerDefinition>().RegisterModel("Indexers");
Mapper.Entity<IndexerDefinition>().RegisterModel("Indexers")
.Ignore(s => s.Protocol);
Mapper.Entity<ScheduledTask>().RegisterModel("ScheduledTasks");
Mapper.Entity<NotificationDefinition>().RegisterModel("Notifications");
Mapper.Entity<MetadataDefinition>().RegisterModel("Metadata");

View File

@ -5,17 +5,18 @@ using NzbDrone.Core.Download;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Queue;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class NotInQueueSpecification : IDecisionEngineSpecification
{
private readonly IProvideDownloadClient _downloadClientProvider;
private readonly IQueueService _queueService;
private readonly Logger _logger;
public NotInQueueSpecification(IProvideDownloadClient downloadClientProvider, Logger logger)
public NotInQueueSpecification(IQueueService queueService, Logger logger)
{
_downloadClientProvider = downloadClientProvider;
_queueService = queueService;
_logger = logger;
}
@ -29,15 +30,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
var downloadClient = _downloadClientProvider.GetDownloadClient();
if (downloadClient == null)
{
_logger.Warn("Download client isn't configured yet.");
return true;
}
var queue = downloadClient.GetQueue().Select(q => q.RemoteEpisode);
var queue = _queueService.GetQueue().Select(q => q.RemoteEpisode);
if (IsInQueue(subject, queue))
{

View File

@ -1,3 +1,4 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.Sabnzbd;
@ -41,9 +42,9 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
return true;
}
var downloadClient = _downloadClientProvider.GetDownloadClient();
var downloadClients = _downloadClientProvider.GetDownloadClients();
if (downloadClient != null && downloadClient.GetType() == typeof (Sabnzbd))
foreach (var downloadClient in downloadClients.OfType<Sabnzbd>())
{
_logger.Debug("Performing history status check on report");
foreach (var episode in subject.Episodes)

View File

@ -2,7 +2,7 @@
namespace NzbDrone.Core.Download
{
public class CheckForFailedDownloadCommand : Command
public class CheckForFinishedDownloadCommand : Command
{
}

View File

@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Download.Clients.Blackhole
{
public class Blackhole : DownloadClientBase<FolderSettings>, IExecute<TestBlackholeCommand>
{
private readonly IDiskProvider _diskProvider;
private readonly IHttpProvider _httpProvider;
private readonly Logger _logger;
public Blackhole(IDiskProvider diskProvider, IHttpProvider httpProvider, Logger logger)
{
_diskProvider = diskProvider;
_httpProvider = httpProvider;
_logger = logger;
}
public override string DownloadNzb(RemoteEpisode remoteEpisode)
{
var url = remoteEpisode.Release.DownloadUrl;
var title = remoteEpisode.Release.Title;
title = FileNameBuilder.CleanFilename(title);
var filename = Path.Combine(Settings.Folder, title + ".nzb");
_logger.Debug("Downloading NZB from: {0} to: {1}", url, filename);
_httpProvider.DownloadFile(url, filename);
_logger.Debug("NZB Download succeeded, saved to: {0}", filename);
return null;
}
public override IEnumerable<QueueItem> GetQueue()
{
return new QueueItem[0];
}
public override IEnumerable<HistoryItem> GetHistory(int start = 0, int limit = 10)
{
return new HistoryItem[0];
}
public override void RemoveFromQueue(string id)
{
}
public override void RemoveFromHistory(string id)
{
}
public override void RetryDownload(string id)
{
throw new NotImplementedException();
}
public override void Test()
{
PerformTest(Settings.Folder);
}
private void PerformTest(string folder)
{
var testPath = Path.Combine(folder, "drone_test.txt");
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
_diskProvider.DeleteFile(testPath);
}
public void Execute(TestBlackholeCommand message)
{
PerformTest(message.Folder);
}
}
}

View File

@ -1,32 +0,0 @@
using System;
using FluentValidation;
using FluentValidation.Results;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation.Paths;
namespace NzbDrone.Core.Download.Clients
{
public class FolderSettingsValidator : AbstractValidator<FolderSettings>
{
public FolderSettingsValidator()
{
//Todo: Validate that the path actually exists
RuleFor(c => c.Folder).IsValidPath();
}
}
public class FolderSettings : IProviderConfig
{
private static readonly FolderSettingsValidator Validator = new FolderSettingsValidator();
[FieldDefinition(0, Label = "Folder", Type = FieldType.Path)]
public String Folder { get; set; }
public ValidationResult Validate()
{
return Validator.Validate(this);
}
}
}

View File

@ -5,15 +5,20 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class NzbgetQueueItem
{
private string _nzbName;
public Int32 NzbId { get; set; }
public Int32 FirstId { get; set; }
public Int32 LastId { get; set; }
public string NzbName { get; set; }
public String NzbName { get; set; }
public String Category { get; set; }
public Int32 FileSizeMb { get; set; }
public Int32 RemainingSizeMb { get; set; }
public Int32 PausedSizeMb { get; set; }
public UInt32 FileSizeLo { get; set; }
public UInt32 FileSizeHi { get; set; }
public UInt32 RemainingSizeLo { get; set; }
public UInt32 RemainingSizeHi { get; set; }
public UInt32 PausedSizeLo { get; set; }
public UInt32 PausedSizeHi { get; set; }
public Int32 MinPriority { get; set; }
public Int32 MaxPriority { get; set; }
public Int32 ActiveDownloads { get; set; }
public List<NzbgetParameter> Parameters { get; set; }
}
}

View File

@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
@ -14,22 +17,28 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
public class Nzbget : DownloadClientBase<NzbgetSettings>, IExecute<TestNzbgetCommand>
{
private readonly INzbgetProxy _proxy;
private readonly IParsingService _parsingService;
private readonly IHttpProvider _httpProvider;
private readonly Logger _logger;
public Nzbget(INzbgetProxy proxy,
IConfigService configService,
IParsingService parsingService,
IHttpProvider httpProvider,
Logger logger)
: base(configService, parsingService, logger)
{
_proxy = proxy;
_parsingService = parsingService;
_httpProvider = httpProvider;
_logger = logger;
}
public override string DownloadNzb(RemoteEpisode remoteEpisode)
public override DownloadProtocol Protocol
{
get
{
return DownloadProtocol.Usenet;
}
}
public override string Download(RemoteEpisode remoteEpisode)
{
var url = remoteEpisode.Release.DownloadUrl;
var title = remoteEpisode.Release.Title + ".nzb";
@ -48,80 +57,129 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
}
}
public override IEnumerable<QueueItem> GetQueue()
private IEnumerable<DownloadClientItem> GetQueue()
{
NzbgetGlobalStatus globalStatus;
List<NzbgetQueueItem> queue;
Dictionary<Int32, NzbgetPostQueueItem> postQueue;
try
{
globalStatus = _proxy.GetGlobalStatus(Settings);
queue = _proxy.GetQueue(Settings);
postQueue = _proxy.GetPostQueue(Settings).ToDictionary(v => v.NzbId);
}
catch (DownloadClientException ex)
{
_logger.ErrorException(ex.Message, ex);
return Enumerable.Empty<QueueItem>();
return Enumerable.Empty<DownloadClientItem>();
}
var queueItems = new List<QueueItem>();
var queueItems = new List<DownloadClientItem>();
Int64 totalRemainingSize = 0;
foreach (var item in queue)
{
var postQueueItem = postQueue.GetValueOrDefault(item.NzbId);
var totalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo);
var pausedSize = MakeInt64(item.PausedSizeHi, item.PausedSizeLo);
var remainingSize = MakeInt64(item.RemainingSizeHi, item.RemainingSizeLo);
var droneParameter = item.Parameters.SingleOrDefault(p => p.Name == "drone");
var queueItem = new QueueItem();
queueItem.Id = droneParameter == null ? item.NzbId.ToString() : droneParameter.Value.ToString();
var queueItem = new DownloadClientItem();
queueItem.DownloadClientId = droneParameter == null ? item.NzbId.ToString() : droneParameter.Value.ToString();
queueItem.Title = item.NzbName;
queueItem.Size = item.FileSizeMb;
queueItem.Sizeleft = item.RemainingSizeMb;
queueItem.Status = item.FileSizeMb == item.PausedSizeMb ? "paused" : "queued";
queueItem.TotalSize = totalSize;
queueItem.Category = item.Category;
var parsedEpisodeInfo = Parser.Parser.ParseTitle(queueItem.Title);
if (parsedEpisodeInfo == null) continue;
if (postQueueItem != null)
{
queueItem.Status = DownloadItemStatus.Downloading;
queueItem.Message = postQueueItem.ProgressLabel;
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0);
if (remoteEpisode.Series == null) continue;
if (postQueueItem.StageProgress != 0)
{
queueItem.RemainingTime = TimeSpan.FromSeconds(postQueueItem.StageTimeSec * 1000 / postQueueItem.StageProgress - postQueueItem.StageTimeSec);
}
}
else if (globalStatus.DownloadPaused || remainingSize == pausedSize)
{
queueItem.Status = DownloadItemStatus.Paused;
queueItem.RemainingSize = remainingSize;
}
else
{
if (item.ActiveDownloads == 0 && remainingSize != 0)
{
queueItem.Status = DownloadItemStatus.Queued;
}
else
{
queueItem.Status = DownloadItemStatus.Downloading;
}
queueItem.RemainingSize = remainingSize - pausedSize;
if (globalStatus.DownloadRate != 0)
{
queueItem.RemainingTime = TimeSpan.FromSeconds((totalRemainingSize + queueItem.RemainingSize) / globalStatus.DownloadRate);
totalRemainingSize += queueItem.RemainingSize;
}
}
queueItem.RemoteEpisode = remoteEpisode;
queueItems.Add(queueItem);
}
return queueItems;
}
public override IEnumerable<HistoryItem> GetHistory(int start = 0, int limit = 10)
private IEnumerable<DownloadClientItem> GetHistory()
{
List<NzbgetHistoryItem> history;
try
{
history = _proxy.GetHistory(Settings);
history = _proxy.GetHistory(Settings).Take(_configService.DownloadClientHistoryLimit).ToList();
}
catch (DownloadClientException ex)
{
_logger.ErrorException(ex.Message, ex);
return Enumerable.Empty<HistoryItem>();
return Enumerable.Empty<DownloadClientItem>();
}
var historyItems = new List<HistoryItem>();
var successStatues = new[] {"SUCCESS", "NONE"};
var historyItems = new List<DownloadClientItem>();
var successStatus = new[] {"SUCCESS", "NONE"};
foreach (var item in history)
{
var droneParameter = item.Parameters.SingleOrDefault(p => p.Name == "drone");
var status = successStatues.Contains(item.ParStatus) &&
successStatues.Contains(item.ScriptStatus)
? HistoryStatus.Completed
: HistoryStatus.Failed;
var historyItem = new HistoryItem();
historyItem.Id = droneParameter == null ? item.Id.ToString() : droneParameter.Value.ToString();
var historyItem = new DownloadClientItem();
historyItem.DownloadClient = Definition.Name;
historyItem.DownloadClientId = droneParameter == null ? item.Id.ToString() : droneParameter.Value.ToString();
historyItem.Title = item.Name;
historyItem.Size = item.FileSizeMb.ToString(); //Why is this a string?
historyItem.DownloadTime = 0;
historyItem.Storage = item.DestDir;
historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo);
historyItem.OutputPath = item.DestDir;
historyItem.Category = item.Category;
historyItem.Message = String.Format("PAR Status: {0} - Script Status: {1}", item.ParStatus, item.ScriptStatus);
historyItem.Status = status;
historyItem.Message = String.Format("PAR Status: {0} - Unpack Status: {1} - Move Status: {2} - Script Status: {3} - Delete Status: {4} - Mark Status: {5}", item.ParStatus, item.UnpackStatus, item.MoveStatus, item.ScriptStatus, item.DeleteStatus, item.MarkStatus);
historyItem.Status = DownloadItemStatus.Completed;
historyItem.RemainingTime = TimeSpan.Zero;
if (item.DeleteStatus == "MANUAL")
{
continue;
}
if (!successStatus.Contains(item.ParStatus) ||
!successStatus.Contains(item.UnpackStatus) ||
!successStatus.Contains(item.MoveStatus) ||
!successStatus.Contains(item.ScriptStatus))
{
historyItem.Status = DownloadItemStatus.Failed;
}
historyItems.Add(historyItem);
}
@ -129,12 +187,20 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
return historyItems;
}
public override void RemoveFromQueue(string id)
public override IEnumerable<DownloadClientItem> GetItems()
{
throw new NotImplementedException();
foreach (var downloadClientItem in GetQueue().Concat(GetHistory()))
{
if (downloadClientItem.Category != Settings.TvCategory) continue;
downloadClientItem.RemoteEpisode = GetRemoteEpisode(downloadClientItem.Title);
if (downloadClientItem.RemoteEpisode == null) continue;
yield return downloadClientItem;
}
}
public override void RemoveFromHistory(string id)
public override void RemoveItem(string id)
{
_proxy.RemoveFromHistory(id, Settings);
}
@ -144,22 +210,94 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
_proxy.RetryDownload(id, Settings);
}
public override void Test()
public override DownloadClientStatus GetStatus()
{
_proxy.GetVersion(Settings);
var config = _proxy.GetConfig(Settings);
var category = GetCategories(config).FirstOrDefault(v => v.Name == Settings.TvCategory);
var status = new DownloadClientStatus
{
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
};
if (category != null)
{
status.OutputRootFolders = new List<string> { category.DestDir };
}
return status;
}
private VersionResponse GetVersion(string host = null, int port = 0, string username = null, string password = null)
protected IEnumerable<NzbgetCategory> GetCategories(Dictionary<String, String> config)
{
for (int i = 1; i < 100; i++)
{
var name = config.GetValueOrDefault("Category" + i + ".Name");
if (name == null) yield break;
var destDir = config.GetValueOrDefault("Category" + i + ".DestDir");
if (destDir.IsNullOrWhiteSpace())
{
var mainDir = config.GetValueOrDefault("MainDir");
destDir = config.GetValueOrDefault("DestDir", String.Empty).Replace("${MainDir}", mainDir);
if (config.GetValueOrDefault("AppendCategoryDir", "yes") == "yes")
{
destDir = Path.Combine(destDir, name);
}
}
yield return new NzbgetCategory
{
Name = name,
DestDir = destDir,
Unpack = config.GetValueOrDefault("Category" + i + ".Unpack") == "yes",
DefScript = config.GetValueOrDefault("Category" + i + ".DefScript"),
Aliases = config.GetValueOrDefault("Category" + i + ".Aliases"),
};
}
}
private String GetVersion(string host = null, int port = 0, string username = null, string password = null)
{
return _proxy.GetVersion(Settings);
}
public override void Test(NzbgetSettings settings)
{
_proxy.GetVersion(settings);
var config = _proxy.GetConfig(settings);
var categories = GetCategories(config);
if (!categories.Any(v => v.Name == settings.TvCategory))
{
throw new ApplicationException("Category does not exist");
}
}
public void Execute(TestNzbgetCommand message)
{
var settings = new NzbgetSettings();
settings.InjectFrom(message);
_proxy.GetVersion(settings);
Test(settings);
}
// Javascript doesn't support 64 bit integers natively so json officially doesn't either.
// NzbGet api thus sends it in two 32 bit chunks. Here we join the two chunks back together.
// Simplified decimal example: "42" splits into "4" and "2". To join them I shift (<<) the "4" 1 digit to the left = "40". combine it with "2". which becomes "42" again.
private Int64 MakeInt64(UInt32 high, UInt32 low)
{
Int64 result = high;
result = (result << 32) | (Int64)low;
return result;
}
}
}

View File

@ -1,10 +0,0 @@
using System;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class NzbgetBooleanResponse
{
public String Version { get; set; }
public Boolean Result { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class NzbgetCategory
{
public String Name { get; set; }
public String DestDir { get; set; }
public Boolean Unpack { get; set; }
public String DefScript { get; set; }
public String Aliases { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class NzbgetConfigItem
{
public String Name { get; set; }
public String Value { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class NzbgetGlobalStatus
{
public UInt32 RemainingSizeLo { get; set; }
public UInt32 RemainingSizeHi { get; set; }
public UInt32 DownloadedSizeLo { get; set; }
public UInt32 DownloadedSizeHi { get; set; }
public UInt32 DownloadRate { get; set; }
public UInt32 AverageDownloadRate { get; set; }
public UInt32 DownloadLimit { get; set; }
public Boolean DownloadPaused { get; set; }
}
}

View File

@ -5,13 +5,17 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class NzbgetHistoryItem
{
private string _nzbName;
public Int32 Id { get; set; }
public String Name { get; set; }
public String Category { get; set; }
public Int32 FileSizeMb { get; set; }
public UInt32 FileSizeLo { get; set; }
public UInt32 FileSizeHi { get; set; }
public String ParStatus { get; set; }
public String UnpackStatus { get; set; }
public String MoveStatus { get; set; }
public String ScriptStatus { get; set; }
public String DeleteStatus { get; set; }
public String MarkStatus { get; set; }
public String DestDir { get; set; }
public List<NzbgetParameter> Parameters { get; set; }
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class NzbgetPostQueueItem
{
public Int32 NzbId { get; set; }
public String NzbName { get; set; }
public String Stage { get; set; }
public String ProgressLabel { get; set; }
public Int32 FileProgress { get; set; }
public Int32 StageProgress { get; set; }
public Int32 TotalTimeSec { get; set; }
public Int32 StageTimeSec { get; set; }
}
}

View File

@ -13,9 +13,12 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
public interface INzbgetProxy
{
string DownloadNzb(Stream nzb, string title, string category, int priority, NzbgetSettings settings);
NzbgetGlobalStatus GetGlobalStatus(NzbgetSettings settings);
List<NzbgetQueueItem> GetQueue(NzbgetSettings settings);
List<NzbgetPostQueueItem> GetPostQueue(NzbgetSettings settings);
List<NzbgetHistoryItem> GetHistory(NzbgetSettings settings);
VersionResponse GetVersion(NzbgetSettings settings);
String GetVersion(NzbgetSettings settings);
Dictionary<String, String> GetConfig(NzbgetSettings settings);
void RemoveFromHistory(string id, NzbgetSettings settings);
void RetryDownload(string id, NzbgetSettings settings);
}
@ -34,8 +37,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
var parameters = new object[] { title, category, priority, false, Convert.ToBase64String(nzb.ToBytes()) };
var request = BuildRequest(new JsonRequest("append", parameters));
var response = Json.Deserialize<NzbgetBooleanResponse>(ProcessRequest(request, settings));
_logger.Debug("Queue Response: [{0}]", response.Result);
var response = Json.Deserialize<NzbgetResponse<Boolean>>(ProcessRequest(request, settings));
_logger.Trace("Response: [{0}]", response.Result);
if (!response.Result)
{
@ -61,31 +64,53 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
return droneId;
}
public NzbgetGlobalStatus GetGlobalStatus(NzbgetSettings settings)
{
var request = BuildRequest(new JsonRequest("status"));
return Json.Deserialize<NzbgetResponse<NzbgetGlobalStatus>>(ProcessRequest(request, settings)).Result;
}
public List<NzbgetQueueItem> GetQueue(NzbgetSettings settings)
{
var request = BuildRequest(new JsonRequest("listgroups"));
return Json.Deserialize<NzbgetListResponse<NzbgetQueueItem>>(ProcessRequest(request, settings)).QueueItems;
return Json.Deserialize<NzbgetResponse<List<NzbgetQueueItem>>>(ProcessRequest(request, settings)).Result;
}
public List<NzbgetPostQueueItem> GetPostQueue(NzbgetSettings settings)
{
var request = BuildRequest(new JsonRequest("postqueue"));
return Json.Deserialize<NzbgetResponse<List<NzbgetPostQueueItem>>>(ProcessRequest(request, settings)).Result;
}
public List<NzbgetHistoryItem> GetHistory(NzbgetSettings settings)
{
var request = BuildRequest(new JsonRequest("history"));
return Json.Deserialize<NzbgetListResponse<NzbgetHistoryItem>>(ProcessRequest(request, settings)).QueueItems;
return Json.Deserialize<NzbgetResponse<List<NzbgetHistoryItem>>>(ProcessRequest(request, settings)).Result;
}
public VersionResponse GetVersion(NzbgetSettings settings)
public String GetVersion(NzbgetSettings settings)
{
var request = BuildRequest(new JsonRequest("version"));
return Json.Deserialize<VersionResponse>(ProcessRequest(request, settings));
return Json.Deserialize<NzbgetResponse<String>>(ProcessRequest(request, settings)).Version;
}
public Dictionary<String, String> GetConfig(NzbgetSettings settings)
{
var request = BuildRequest(new JsonRequest("config"));
return Json.Deserialize<NzbgetResponse<List<NzbgetConfigItem>>>(ProcessRequest(request, settings)).Result.ToDictionary(v => v.Name, v => v.Value);
}
public void RemoveFromHistory(string id, NzbgetSettings settings)
{
var history = GetHistory(settings);
var item = history.SingleOrDefault(h => h.Parameters.SingleOrDefault(p => p.Name == "drone") != null);
var item = history.SingleOrDefault(h => h.Parameters.Any(p => p.Name == "drone" && id == (p.Value as string)));
if (item == null)
{
@ -120,7 +145,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{
var parameters = new object[] { command, offset, editText, id };
var request = BuildRequest(new JsonRequest("editqueue", parameters));
var response = Json.Deserialize<NzbgetBooleanResponse>(ProcessRequest(request, settings));
var response = Json.Deserialize<NzbgetResponse<Boolean>>(ProcessRequest(request, settings));
return response.Result;
}
@ -129,7 +154,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{
var client = BuildClient(settings);
var response = client.Execute(restRequest);
_logger.Debug("Response: {0}", response.Content);
_logger.Trace("Response: {0}", response.Content);
CheckForError(response);
@ -145,6 +170,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
settings.Host,
settings.Port);
_logger.Debug("Url: " + url);
var client = new RestClient(url);
client.Authenticator = new HttpBasicAuthenticator(settings.Username, settings.Password);

View File

@ -4,11 +4,11 @@ using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class NzbgetListResponse<T>
public class NzbgetResponse<T>
{
public String Version { get; set; }
[JsonProperty(PropertyName = "result")]
public List<T> QueueItems { get; set; }
public T Result { get; set; }
}
}

View File

@ -13,9 +13,14 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
}
}
public String Host { get; set; }
public Int32 Port { get; set; }
public String Username { get; set; }
public String Password { get; set; }
public String TvCategory { get; set; }
public Int32 RecentTvPriority { get; set; }
public Int32 OlderTvPriority { get; set; }
public Boolean UseSsl { get; set; }
}
}

View File

@ -1,10 +0,0 @@
using System;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class VersionResponse
{
public String Version { get; set; }
public String Result { get; set; }
}
}

View File

@ -7,42 +7,55 @@ using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using Omu.ValueInjecter;
namespace NzbDrone.Core.Download.Clients.Pneumatic
{
public class Pneumatic : DownloadClientBase<FolderSettings>, IExecute<TestPneumaticCommand>
public class Pneumatic : DownloadClientBase<PneumaticSettings>, IExecute<TestPneumaticCommand>
{
private readonly IConfigService _configService;
private readonly IHttpProvider _httpProvider;
private readonly IDiskProvider _diskProvider;
private static readonly Logger logger = NzbDroneLogger.GetLogger();
public Pneumatic(IConfigService configService, IHttpProvider httpProvider,
IDiskProvider diskProvider)
public Pneumatic(IHttpProvider httpProvider,
IDiskProvider diskProvider,
IConfigService configService,
IParsingService parsingService,
Logger logger)
: base(configService, parsingService, logger)
{
_configService = configService;
_httpProvider = httpProvider;
_diskProvider = diskProvider;
}
public override string DownloadNzb(RemoteEpisode remoteEpisode)
public override DownloadProtocol Protocol
{
get
{
return DownloadProtocol.Usenet;
}
}
public override string Download(RemoteEpisode remoteEpisode)
{
var url = remoteEpisode.Release.DownloadUrl;
var title = remoteEpisode.Release.Title;
if (remoteEpisode.ParsedEpisodeInfo.FullSeason)
{
throw new NotImplementedException("Full season releases are not supported with Pneumatic.");
throw new NotSupportedException("Full season releases are not supported with Pneumatic.");
}
title = FileNameBuilder.CleanFilename(title);
//Save to the Pneumatic directory (The user will need to ensure its accessible by XBMC)
var filename = Path.Combine(Settings.Folder, title + ".nzb");
var filename = Path.Combine(Settings.NzbFolder, title + ".nzb");
logger.Debug("Downloading NZB from: {0} to: {1}", url, filename);
_httpProvider.DownloadFile(url, filename);
@ -59,39 +72,41 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
{
get
{
return !string.IsNullOrWhiteSpace(Settings.Folder);
return !string.IsNullOrWhiteSpace(Settings.NzbFolder);
}
}
public override IEnumerable<QueueItem> GetQueue()
public override IEnumerable<DownloadClientItem> GetItems()
{
return new QueueItem[0];
return new DownloadClientItem[0];
}
public override IEnumerable<HistoryItem> GetHistory(int start = 0, int limit = 10)
{
return new HistoryItem[0];
}
public override void RemoveFromQueue(string id)
{
}
public override void RemoveFromHistory(string id)
public override void RemoveItem(string id)
{
throw new NotSupportedException();
}
public override void RetryDownload(string id)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public override void Test()
public override DownloadClientStatus GetStatus()
{
PerformTest(Settings.Folder);
var status = new DownloadClientStatus
{
IsLocalhost = true
};
return status;
}
private void PerformTest(string folder)
public override void Test(PneumaticSettings settings)
{
PerformWriteTest(settings.NzbFolder);
}
private void PerformWriteTest(string folder)
{
var testPath = Path.Combine(folder, "drone_test.txt");
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
@ -100,7 +115,10 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
public void Execute(TestPneumaticCommand message)
{
PerformTest(message.Folder);
var settings = new PneumaticSettings();
settings.InjectFrom(message);
Test(settings);
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using FluentValidation;
using FluentValidation.Results;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation.Paths;
namespace NzbDrone.Core.Download.Clients.Pneumatic
{
public class PneumaticSettingsValidator : AbstractValidator<PneumaticSettings>
{
public PneumaticSettingsValidator()
{
//Todo: Validate that the path actually exists
RuleFor(c => c.NzbFolder).IsValidPath();
}
}
public class PneumaticSettings : IProviderConfig
{
private static readonly PneumaticSettingsValidator Validator = new PneumaticSettingsValidator();
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path)]
public String NzbFolder { get; set; }
public ValidationResult Validate()
{
return Validator.Validate(this);
}
}
}

View File

@ -1,10 +1,12 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
@ -15,25 +17,28 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
public class Sabnzbd : DownloadClientBase<SabnzbdSettings>, IExecute<TestSabnzbdCommand>
{
private readonly IHttpProvider _httpProvider;
private readonly IParsingService _parsingService;
private readonly ISabnzbdProxy _proxy;
private readonly ICached<IEnumerable<QueueItem>> _queueCache;
private readonly Logger _logger;
public Sabnzbd(IHttpProvider httpProvider,
ICacheManager cacheManager,
IParsingService parsingService,
ISabnzbdProxy proxy,
IConfigService configService,
IParsingService parsingService,
Logger logger)
: base(configService, parsingService, logger)
{
_httpProvider = httpProvider;
_parsingService = parsingService;
_proxy = proxy;
_queueCache = cacheManager.GetCache<IEnumerable<QueueItem>>(GetType(), "queue");
_logger = logger;
}
public override string DownloadNzb(RemoteEpisode remoteEpisode)
public override DownloadProtocol Protocol
{
get
{
return DownloadProtocol.Usenet;
}
}
public override string Download(RemoteEpisode remoteEpisode)
{
var url = remoteEpisode.Release.DownloadUrl;
var title = remoteEpisode.Release.Title;
@ -54,76 +59,118 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
}
}
public override IEnumerable<QueueItem> GetQueue()
private IEnumerable<DownloadClientItem> GetQueue()
{
return _queueCache.Get("queue", () =>
SabnzbdQueue sabQueue;
try
{
SabnzbdQueue sabQueue;
sabQueue = _proxy.GetQueue(0, 0, Settings);
}
catch (DownloadClientException ex)
{
_logger.ErrorException(ex.Message, ex);
return Enumerable.Empty<DownloadClientItem>();
}
try
var queueItems = new List<DownloadClientItem>();
foreach (var sabQueueItem in sabQueue.Items)
{
var queueItem = new DownloadClientItem();
queueItem.DownloadClient = Definition.Name;
queueItem.DownloadClientId = sabQueueItem.Id;
queueItem.Category = sabQueueItem.Category;
queueItem.Title = sabQueueItem.Title;
queueItem.TotalSize = (long)(sabQueueItem.Size * 1024 * 1024);
queueItem.RemainingSize = (long)(sabQueueItem.Sizeleft * 1024 * 1024);
queueItem.RemainingTime = sabQueueItem.Timeleft;
if (sabQueue.Paused || sabQueueItem.Status == SabnzbdDownloadStatus.Paused)
{
sabQueue = _proxy.GetQueue(0, 0, Settings);
queueItem.Status = DownloadItemStatus.Paused;
queueItem.RemainingTime = null;
}
catch (DownloadClientException ex)
else if (sabQueueItem.Status == SabnzbdDownloadStatus.Queued || sabQueueItem.Status == SabnzbdDownloadStatus.Grabbing)
{
_logger.ErrorException(ex.Message, ex);
return Enumerable.Empty<QueueItem>();
queueItem.Status = DownloadItemStatus.Queued;
}
else
{
queueItem.Status = DownloadItemStatus.Downloading;
}
var queueItems = new List<QueueItem>();
foreach (var sabQueueItem in sabQueue.Items)
if (queueItem.Title.StartsWith("ENCRYPTED /"))
{
var queueItem = new QueueItem();
queueItem.Id = sabQueueItem.Id;
queueItem.Title = sabQueueItem.Title;
queueItem.Size = sabQueueItem.Size;
queueItem.Sizeleft = sabQueueItem.Sizeleft;
queueItem.Timeleft = sabQueueItem.Timeleft;
queueItem.Status = sabQueueItem.Status;
var parsedEpisodeInfo = Parser.Parser.ParseTitle(queueItem.Title.Replace("ENCRYPTED / ", ""));
if (parsedEpisodeInfo == null) continue;
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0);
if (remoteEpisode.Series == null) continue;
queueItem.RemoteEpisode = remoteEpisode;
queueItems.Add(queueItem);
queueItem.Title = queueItem.Title.Substring(11);
queueItem.IsEncrypted = true;
}
return queueItems;
}, TimeSpan.FromSeconds(10));
queueItems.Add(queueItem);
}
return queueItems;
}
public override IEnumerable<HistoryItem> GetHistory(int start = 0, int limit = 10)
private IEnumerable<DownloadClientItem> GetHistory()
{
SabnzbdHistory sabHistory;
try
{
sabHistory = _proxy.GetHistory(start, limit, Settings);
sabHistory = _proxy.GetHistory(0, _configService.DownloadClientHistoryLimit, Settings);
}
catch (DownloadClientException ex)
{
_logger.ErrorException(ex.Message, ex);
return Enumerable.Empty<HistoryItem>();
return Enumerable.Empty<DownloadClientItem>();
}
var historyItems = new List<HistoryItem>();
var historyItems = new List<DownloadClientItem>();
foreach (var sabHistoryItem in sabHistory.Items)
{
var historyItem = new HistoryItem();
historyItem.Id = sabHistoryItem.Id;
historyItem.Title = sabHistoryItem.Title;
historyItem.Size = sabHistoryItem.Size;
historyItem.DownloadTime = sabHistoryItem.DownloadTime;
historyItem.Storage = sabHistoryItem.Storage;
historyItem.Category = sabHistoryItem.Category;
historyItem.Message = sabHistoryItem.FailMessage;
historyItem.Status = sabHistoryItem.Status == "Failed" ? HistoryStatus.Failed : HistoryStatus.Completed;
var historyItem = new DownloadClientItem
{
DownloadClient = Definition.Name,
DownloadClientId = sabHistoryItem.Id,
Category = sabHistoryItem.Category,
Title = sabHistoryItem.Title,
TotalSize = sabHistoryItem.Size,
RemainingSize = 0,
DownloadTime = TimeSpan.FromSeconds(sabHistoryItem.DownloadTime),
RemainingTime = TimeSpan.Zero,
Message = sabHistoryItem.FailMessage
};
if (sabHistoryItem.Status == SabnzbdDownloadStatus.Failed)
{
historyItem.Status = DownloadItemStatus.Failed;
}
else if (sabHistoryItem.Status == SabnzbdDownloadStatus.Completed)
{
historyItem.Status = DownloadItemStatus.Completed;
}
else // Verifying/Moving etc
{
historyItem.Status = DownloadItemStatus.Downloading;
}
if (!sabHistoryItem.Storage.IsNullOrWhiteSpace())
{
var parent = Directory.GetParent(sabHistoryItem.Storage);
if (parent != null && parent.Name == sabHistoryItem.Title)
{
historyItem.OutputPath = parent.FullName;
}
else
{
historyItem.OutputPath = sabHistoryItem.Storage;
}
}
historyItems.Add(historyItem);
}
@ -131,14 +178,29 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return historyItems;
}
public override void RemoveFromQueue(string id)
public override IEnumerable<DownloadClientItem> GetItems()
{
_proxy.RemoveFrom("queue", id, Settings);
foreach (var downloadClientItem in GetQueue().Concat(GetHistory()))
{
if (downloadClientItem.Category != Settings.TvCategory) continue;
downloadClientItem.RemoteEpisode = GetRemoteEpisode(downloadClientItem.Title);
if (downloadClientItem.RemoteEpisode == null) continue;
yield return downloadClientItem;
}
}
public override void RemoveFromHistory(string id)
public override void RemoveItem(string id)
{
_proxy.RemoveFrom("history", id, Settings);
if (GetQueue().Any(v => v.DownloadClientId == id))
{
_proxy.RemoveFrom("queue", id, Settings);
}
else
{
_proxy.RemoveFrom("history", id, Settings);
}
}
public override void RetryDownload(string id)
@ -146,9 +208,24 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
_proxy.RetryDownload(id, Settings);
}
public override void Test()
public override DownloadClientStatus GetStatus()
{
_proxy.GetCategories(Settings);
var status = new DownloadClientStatus
{
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
};
return status;
}
public override void Test(SabnzbdSettings settings)
{
var categories = _proxy.GetCategories(settings);
if (!categories.Any(v => v == settings.TvCategory))
{
throw new ApplicationException("Category does not exist");
}
}
public void Execute(TestSabnzbdCommand message)
@ -156,7 +233,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
var settings = new SabnzbdSettings();
settings.InjectFrom(message);
_proxy.GetCategories(settings);
Test(settings);
}
}
}

View File

@ -0,0 +1,22 @@
using System;
namespace NzbDrone.Core.Download.Clients.Sabnzbd
{
public enum SabnzbdDownloadStatus
{
Grabbing,
Queued,
Paused,
Checking,
Downloading,
QuickCheck,
Verifying,
Repairing,
Fetching, // Fetching additional blocks
Extracting,
Moving,
Running, // Running PP Script
Completed,
Failed
}
}

View File

@ -1,4 +1,5 @@
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.Sabnzbd
{
@ -7,7 +8,8 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
[JsonProperty(PropertyName = "fail_message")]
public string FailMessage { get; set; }
public string Size { get; set; }
[JsonProperty(PropertyName = "bytes")]
public Int64 Size { get; set; }
public string Category { get; set; }
[JsonProperty(PropertyName = "nzb_name")]
@ -17,7 +19,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
public int DownloadTime { get; set; }
public string Storage { get; set; }
public string Status { get; set; }
public SabnzbdDownloadStatus Status { get; set; }
[JsonProperty(PropertyName = "nzo_id")]
public string Id { get; set; }

View File

@ -1,12 +1,13 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Download.Clients.Sabnzbd.Responses;
using NzbDrone.Core.Instrumentation.Extensions;
using RestSharp;
namespace NzbDrone.Core.Download.Clients.Sabnzbd
@ -17,7 +18,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
void RemoveFrom(string source, string id, SabnzbdSettings settings);
string ProcessRequest(IRestRequest restRequest, string action, SabnzbdSettings settings);
SabnzbdVersionResponse GetVersion(SabnzbdSettings settings);
SabnzbdCategoryResponse GetCategories(SabnzbdSettings settings);
List<String> GetCategories(SabnzbdSettings settings);
SabnzbdQueue GetQueue(int start, int limit, SabnzbdSettings settings);
SabnzbdHistory GetHistory(int start, int limit, SabnzbdSettings settings);
void RetryDownload(string id, SabnzbdSettings settings);
@ -84,12 +85,12 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return response;
}
public SabnzbdCategoryResponse GetCategories(SabnzbdSettings settings)
public List<String> GetCategories(SabnzbdSettings settings)
{
var request = new RestRequest();
var action = "mode=get_cats";
var response = Json.Deserialize<SabnzbdCategoryResponse>(ProcessRequest(request, action, settings));
var response = Json.Deserialize<SabnzbdCategoryResponse>(ProcessRequest(request, action, settings)).Categories;
return response;
}
@ -135,7 +136,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
action,
authentication);
_logger.Debug(url);
_logger.Debug("Url: " + url);
return new RestClient(url);
}
@ -167,7 +168,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
result.Error = response.Content.Replace("error: ", "");
}
if (result.Failed)
throw new DownloadClientException("Error response received from SABnzbd: {0}", result.Error);
}

View File

@ -6,7 +6,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{
public class SabnzbdQueueItem
{
public string Status { get; set; }
public SabnzbdDownloadStatus Status { get; set; }
public int Index { get; set; }
[JsonConverter(typeof(SabnzbdQueueTimeConverter))]
@ -15,8 +15,6 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
[JsonProperty(PropertyName = "mb")]
public decimal Size { get; set; }
private string _title;
[JsonProperty(PropertyName = "filename")]
public string Title { get; set; }

View File

@ -13,11 +13,15 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
}
}
public String Host { get; set; }
public Int32 Port { get; set; }
public String ApiKey { get; set; }
public String Username { get; set; }
public String Password { get; set; }
public String TvCategory { get; set; }
public Int32 RecentTvPriority { get; set; }
public Int32 OlderTvPriority { get; set; }
public Boolean UseSsl { get; set; }
}
}

View File

@ -1,9 +1,9 @@
using System;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Download.Clients.Blackhole
namespace NzbDrone.Core.Download.Clients.UsenetBlackhole
{
public class TestBlackholeCommand : Command
public class TestUsenetBlackholeCommand : Command
{
public override bool SendUpdatesToClient
{
@ -13,6 +13,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
}
}
public String Folder { get; set; }
public String NzbFolder { get; set; }
public String WatchFolder { get; set; }
}
}

View File

@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.MediaFiles;
using Omu.ValueInjecter;
namespace NzbDrone.Core.Download.Clients.UsenetBlackhole
{
public class UsenetBlackhole : DownloadClientBase<UsenetBlackholeSettings>, IExecute<TestUsenetBlackholeCommand>
{
private readonly IDiskProvider _diskProvider;
private readonly IDiskScanService _diskScanService;
private readonly IHttpProvider _httpProvider;
public UsenetBlackhole(IDiskProvider diskProvider,
IDiskScanService diskScanService,
IHttpProvider httpProvider,
IConfigService configService,
IParsingService parsingService,
Logger logger)
: base(configService, parsingService, logger)
{
_diskProvider = diskProvider;
_diskScanService = diskScanService;
_httpProvider = httpProvider;
}
public override DownloadProtocol Protocol
{
get
{
return DownloadProtocol.Usenet;
}
}
public override string Download(RemoteEpisode remoteEpisode)
{
var url = remoteEpisode.Release.DownloadUrl;
var title = remoteEpisode.Release.Title;
title = FileNameBuilder.CleanFilename(title);
var filename = Path.Combine(Settings.NzbFolder, title + ".nzb");
_logger.Debug("Downloading NZB from: {0} to: {1}", url, filename);
_httpProvider.DownloadFile(url, filename);
_logger.Debug("NZB Download succeeded, saved to: {0}", filename);
return null;
}
public override IEnumerable<DownloadClientItem> GetItems()
{
foreach (var folder in _diskProvider.GetDirectories(Settings.WatchFolder))
{
var title = FileNameBuilder.CleanFilename(Path.GetFileName(folder));
var files = _diskProvider.GetFiles(folder, SearchOption.AllDirectories);
var historyItem = new DownloadClientItem
{
DownloadClient = Definition.Name,
DownloadClientId = Definition.Name + "_" + Path.GetFileName(folder) + "_" + _diskProvider.FolderGetCreationTimeUtc(folder).Ticks,
Title = title,
TotalSize = files.Select(_diskProvider.GetFileSize).Sum(),
OutputPath = folder
};
if (files.Any(_diskProvider.IsFileLocked))
{
historyItem.Status = DownloadItemStatus.Downloading;
}
else
{
historyItem.Status = DownloadItemStatus.Completed;
historyItem.RemainingTime = TimeSpan.Zero;
}
historyItem.RemoteEpisode = GetRemoteEpisode(historyItem.Title);
if (historyItem.RemoteEpisode == null) continue;
yield return historyItem;
}
foreach (var videoFile in _diskScanService.GetVideoFiles(Settings.WatchFolder, false))
{
var title = FileNameBuilder.CleanFilename(Path.GetFileName(videoFile));
var historyItem = new DownloadClientItem
{
DownloadClient = Definition.Name,
DownloadClientId = Definition.Name + "_" + Path.GetFileName(videoFile) + "_" + _diskProvider.FileGetLastWriteUtc(videoFile).Ticks,
Title = title,
TotalSize = _diskProvider.GetFileSize(videoFile),
OutputPath = videoFile
};
if (_diskProvider.IsFileLocked(videoFile))
{
historyItem.Status = DownloadItemStatus.Downloading;
}
else
{
historyItem.Status = DownloadItemStatus.Completed;
}
historyItem.RemoteEpisode = GetRemoteEpisode(historyItem.Title);
if (historyItem.RemoteEpisode == null) continue;
yield return historyItem;
}
}
public override void RemoveItem(string id)
{
throw new NotSupportedException();
}
public override void RetryDownload(string id)
{
throw new NotSupportedException();
}
public override DownloadClientStatus GetStatus()
{
return new DownloadClientStatus
{
IsLocalhost = true,
OutputRootFolders = new List<string> { Settings.WatchFolder }
};
}
public override void Test(UsenetBlackholeSettings settings)
{
PerformWriteTest(settings.NzbFolder);
PerformWriteTest(settings.WatchFolder);
}
private void PerformWriteTest(string folder)
{
var testPath = Path.Combine(folder, "drone_test.txt");
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
_diskProvider.DeleteFile(testPath);
}
public void Execute(TestUsenetBlackholeCommand message)
{
var settings = new UsenetBlackholeSettings();
settings.InjectFrom(message);
Test(settings);
}
}
}

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