New: It is now possible to use Completed Download Handling with remote download clients by specifying the local mount in settings.

This commit is contained in:
Taloth Saldono 2014-07-05 16:21:44 +02:00
parent 822de39a9e
commit 257cdf9382
8 changed files with 218 additions and 32 deletions

View File

@ -26,14 +26,6 @@ namespace NzbDrone.Common.Test.DiskProviderTests
Subject.GetAvailableSpace(Path.Combine(path, "invalidFolder")).Should().NotBe(0);
}
[Test]
public void should_get_free_space_for_drive_that_doesnt_exist()
{
WindowsOnly();
Assert.Throws<DirectoryNotFoundException>(() => Subject.GetAvailableSpace("J:\\").Should().NotBe(0));
}
[Test]
public void should_be_able_to_check_space_on_ramdrive()
{

View File

@ -1,17 +1,18 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.Nzbget;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Test.Common;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using System.Collections.Generic;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
{
@ -28,7 +29,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new NzbgetSettings
{
Host = "192.168.5.55",
Host = "127.0.0.1",
Port = 2222,
Username = "admin",
Password = "pass",
@ -65,7 +66,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
FileSizeLo = 1000,
Category = "tv",
Name = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE",
DestDir = "somedirectory",
DestDir = "/remote/mount/tv/Droned.S01E01.Pilot.1080p.WEB-DL-DRONE",
Parameters = new List<NzbgetParameter> { new NzbgetParameter { Name = "drone", Value = "id" } },
ParStatus = "SUCCESS",
UnpackStatus = "NONE",
@ -81,6 +82,19 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
{
DownloadRate = 7000000
});
var configItems = new Dictionary<String, String>();
configItems.Add("Category1.Name", "tv");
configItems.Add("Category1.DestDir", @"/remote/mount/tv");
Mocker.GetMock<INzbgetProxy>()
.Setup(v => v.GetConfig(It.IsAny<NzbgetSettings>()))
.Returns(configItems);
}
protected void WithMountPoint(String mountPath)
{
(Subject.Definition.Settings as NzbgetSettings).TvCategoryLocalPath = mountPath;
}
protected void WithFailedDownload()
@ -223,5 +237,40 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
items.Should().BeEmpty();
}
[Test]
public void should_return_status_with_outputdir()
{
var result = Subject.GetStatus();
result.IsLocalhost.Should().BeTrue();
result.OutputRootFolders.Should().NotBeNull();
result.OutputRootFolders.First().Should().Be(@"/remote/mount/tv");
}
[Test]
public void should_return_status_with_mounted_outputdir()
{
WithMountPoint(@"O:\mymount".AsOsAgnostic());
var result = Subject.GetStatus();
result.IsLocalhost.Should().BeTrue();
result.OutputRootFolders.Should().NotBeNull();
result.OutputRootFolders.First().Should().Be(@"O:\mymount".AsOsAgnostic());
}
[Test]
public void should_remap_storage_if_mounted()
{
WithMountPoint(@"O:\mymount".AsOsAgnostic());
WithQueue(null);
WithHistory(_completed);
var result = Subject.GetItems().Single();
result.OutputPath.Should().Be(@"O:\mymount\Droned.S01E01.Pilot.1080p.WEB-DL-DRONE".AsOsAgnostic());
}
}
}

View File

@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new SabnzbdSettings
{
Host = "192.168.5.55",
Host = "127.0.0.1",
Port = 2222,
ApiKey = "5c770e3197e4fe763423ee7c392c25d1",
Username = "admin",
@ -82,10 +82,29 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
Category = "tv",
Id = "sabnzbd_nzb12345",
Title = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE",
Storage = "somedirectory"
Storage = "/remote/mount/vv/Droned.S01E01.Pilot.1080p.WEB-DL-DRONE"
}
}
};
Mocker.GetMock<ISabnzbdProxy>()
.Setup(s => s.GetConfig(It.IsAny<SabnzbdSettings>()))
.Returns(new SabnzbdConfig
{
Misc = new SabnzbdConfigMisc
{
complete_dir = "/remote/mount/"
},
Categories = new List<SabnzbdCategory>
{
new SabnzbdCategory { Name = "tv", Dir = "vv" }
}
});
}
protected void WithMountPoint(String mountPath)
{
(Subject.Definition.Settings as SabnzbdSettings).TvCategoryLocalPath = mountPath;
}
protected void WithFailedDownload()
@ -269,6 +288,19 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
result.OutputPath.Should().Be(@"C:\sorted\Droned.S01E01.Pilot.1080p.WEB-DL-DRONE".AsOsAgnostic());
}
[Test]
public void should_remap_storage_if_mounted()
{
WithMountPoint(@"O:\mymount".AsOsAgnostic());
WithQueue(null);
WithHistory(_completed);
var result = Subject.GetItems().Single();
result.OutputPath.Should().Be(@"O:\mymount\Droned.S01E01.Pilot.1080p.WEB-DL-DRONE".AsOsAgnostic());
}
[Test]
public void should_not_blow_up_if_storage_is_drive_root()
{
@ -281,5 +313,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
result.OutputPath.Should().Be(@"C:\".AsOsAgnostic());
}
[Test]
public void should_return_status_with_outputdir()
{
var result = Subject.GetStatus();
result.IsLocalhost.Should().BeTrue();
result.OutputRootFolders.Should().NotBeNull();
result.OutputRootFolders.First().Should().Be(@"/remote/mount/vv");
}
[Test]
public void should_return_status_with_mounted_outputdir()
{
WithMountPoint(@"O:\mymount".AsOsAgnostic());
var result = Subject.GetStatus();
result.IsLocalhost.Should().BeTrue();
result.OutputRootFolders.Should().NotBeNull();
result.OutputRootFolders.First().Should().Be(@"O:\mymount".AsOsAgnostic());
}
}
}

View File

@ -189,14 +189,36 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
public override IEnumerable<DownloadClientItem> GetItems()
{
Dictionary<String,String> config = null;
NzbgetCategory category = null;
try
{
if (!Settings.TvCategoryLocalPath.IsNullOrWhiteSpace())
{
config = _proxy.GetConfig(Settings);
category = GetCategories(config).FirstOrDefault(v => v.Name == Settings.TvCategory);
}
}
catch (DownloadClientException ex)
{
_logger.ErrorException(ex.Message, ex);
yield break;
}
foreach (var downloadClientItem in GetQueue().Concat(GetHistory()))
{
if (downloadClientItem.Category != Settings.TvCategory) continue;
if (downloadClientItem.Category == Settings.TvCategory)
{
if (category != null)
{
RemapStorage(downloadClientItem, category.DestDir, Settings.TvCategoryLocalPath);
}
downloadClientItem.RemoteEpisode = GetRemoteEpisode(downloadClientItem.Title);
if (downloadClientItem.RemoteEpisode == null) continue;
downloadClientItem.RemoteEpisode = GetRemoteEpisode(downloadClientItem.Title);
if (downloadClientItem.RemoteEpisode == null) continue;
yield return downloadClientItem;
yield return downloadClientItem;
}
}
}
@ -223,7 +245,14 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
if (category != null)
{
status.OutputRootFolders = new List<string> { category.DestDir };
if (Settings.TvCategoryLocalPath.IsNullOrWhiteSpace())
{
status.OutputRootFolders = new List<String> { category.DestDir };
}
else
{
status.OutputRootFolders = new List<String> { Settings.TvCategoryLocalPath };
}
}
return status;

View File

@ -3,6 +3,7 @@ using FluentValidation;
using FluentValidation.Results;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation.Paths;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
@ -14,6 +15,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
RuleFor(c => c.Port).GreaterThan(0);
RuleFor(c => c.Username).NotEmpty().When(c => !String.IsNullOrWhiteSpace(c.Password));
RuleFor(c => c.Password).NotEmpty().When(c => !String.IsNullOrWhiteSpace(c.Username));
RuleFor(c => c.TvCategory).NotEmpty().When(c => !String.IsNullOrWhiteSpace(c.TvCategoryLocalPath));
RuleFor(c => c.TvCategoryLocalPath).IsValidPath().When(c => !String.IsNullOrWhiteSpace(c.TvCategoryLocalPath));
}
}
@ -45,13 +49,16 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox)]
public String TvCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
[FieldDefinition(5, Label = "Category Local Path", Type = FieldType.Textbox, Advanced = true, HelpText = "Local path to the category output dir. Useful if Nzbget runs on another computer.")]
public String TvCategoryLocalPath { get; set; }
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
public Int32 RecentTvPriority { get; set; }
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
[FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public Int32 OlderTvPriority { get; set; }
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)]
[FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox)]
public Boolean UseSsl { get; set; }
public ValidationResult Validate()

View File

@ -182,14 +182,36 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
public override IEnumerable<DownloadClientItem> GetItems()
{
SabnzbdConfig config = null;
SabnzbdCategory category = null;
try
{
if (!Settings.TvCategoryLocalPath.IsNullOrWhiteSpace())
{
config = _proxy.GetConfig(Settings);
category = GetCategories(config).FirstOrDefault(v => v.Name == Settings.TvCategory);
}
}
catch (DownloadClientException ex)
{
_logger.ErrorException(ex.Message, ex);
yield break;
}
foreach (var downloadClientItem in GetQueue().Concat(GetHistory()))
{
if (downloadClientItem.Category != Settings.TvCategory) continue;
if (downloadClientItem.Category == Settings.TvCategory)
{
if (category != null)
{
RemapStorage(downloadClientItem, category.FullPath, Settings.TvCategoryLocalPath);
}
downloadClientItem.RemoteEpisode = GetRemoteEpisode(downloadClientItem.Title);
if (downloadClientItem.RemoteEpisode == null) continue;
downloadClientItem.RemoteEpisode = GetRemoteEpisode(downloadClientItem.Title);
if (downloadClientItem.RemoteEpisode == null) continue;
yield return downloadClientItem;
yield return downloadClientItem;
}
}
}
@ -268,7 +290,14 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
if (category != null)
{
status.OutputRootFolders = new List<String> { category.FullPath };
if (Settings.TvCategoryLocalPath.IsNullOrWhiteSpace())
{
status.OutputRootFolders = new List<String> { category.FullPath };
}
else
{
status.OutputRootFolders = new List<String> { Settings.TvCategoryLocalPath };
}
}
return status;

View File

@ -3,6 +3,7 @@ using FluentValidation;
using FluentValidation.Results;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation.Paths;
namespace NzbDrone.Core.Download.Clients.Sabnzbd
{
@ -21,10 +22,12 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
.WithMessage("Username is required when API key is not configured")
.When(c => String.IsNullOrWhiteSpace(c.ApiKey));
RuleFor(c => c.Password).NotEmpty()
.WithMessage("Password is required when API key is not configured")
.When(c => String.IsNullOrWhiteSpace(c.ApiKey));
RuleFor(c => c.TvCategory).NotEmpty().When(c => !String.IsNullOrWhiteSpace(c.TvCategoryLocalPath));
RuleFor(c => c.TvCategoryLocalPath).IsValidPath().When(c => !String.IsNullOrWhiteSpace(c.TvCategoryLocalPath));
}
}
@ -59,13 +62,16 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox)]
public String TvCategory { get; set; }
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
[FieldDefinition(6, Label = "Category Local Path", Type = FieldType.Textbox, Advanced = true, HelpText = "Local path to the category output dir. Useful if Sabnzbd runs on another computer.")]
public String TvCategoryLocalPath { get; set; }
[FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
public Int32 RecentTvPriority { get; set; }
[FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public Int32 OlderTvPriority { get; set; }
[FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox)]
[FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox)]
public Boolean UseSsl { get; set; }
public ValidationResult Validate()

View File

@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser;
@ -8,6 +10,7 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Configuration;
using NLog;
using FluentValidation.Results;
namespace NzbDrone.Core.Download
{
@ -81,6 +84,23 @@ namespace NzbDrone.Core.Download
return remoteEpisode;
}
protected void RemapStorage(DownloadClientItem downloadClientItem, String remotePath, String localPath)
{
if (downloadClientItem.OutputPath.IsNullOrWhiteSpace() || localPath.IsNullOrWhiteSpace())
{
return;
}
remotePath = remotePath.TrimEnd('/', '\\');
localPath = localPath.TrimEnd('/', '\\');
if (downloadClientItem.OutputPath.StartsWith(remotePath))
{
downloadClientItem.OutputPath = localPath + downloadClientItem.OutputPath.Substring(remotePath.Length);
downloadClientItem.OutputPath = downloadClientItem.OutputPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}
}
protected ValidationFailure TestFolder(String folder, String propertyName, Boolean mustBeWritable = true)
{
if (!_diskProvider.FolderExists(folder))