Added OsPath to handle OS agnostic path handling.
This commit is contained in:
parent
67cd5703a1
commit
8a86b8acdc
|
@ -80,6 +80,7 @@
|
|||
<Compile Include="Http\HttpRequestFixture.cs" />
|
||||
<Compile Include="InstrumentationTests\CleanseLogMessageFixture.cs" />
|
||||
<Compile Include="LevenshteinDistanceFixture.cs" />
|
||||
<Compile Include="OsPathFixture.cs" />
|
||||
<Compile Include="PathExtensionFixture.cs" />
|
||||
<Compile Include="ProcessProviderTests.cs" />
|
||||
<Compile Include="ReflectionExtensions.cs" />
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Test.Common;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace NzbDrone.Common.Test
|
||||
{
|
||||
public class OsPathFixture : TestBase
|
||||
{
|
||||
[TestCase(@"C:\rooted\windows\path\", OsPathKind.Windows)]
|
||||
[TestCase(@"C:\rooted\windows\path", OsPathKind.Windows)]
|
||||
[TestCase(@"C:\", OsPathKind.Windows)]
|
||||
[TestCase(@"C:", OsPathKind.Windows)]
|
||||
[TestCase(@"\\rooted\unc\path\", OsPathKind.Windows)]
|
||||
[TestCase(@"\\rooted\unc\path", OsPathKind.Windows)]
|
||||
[TestCase(@"\relative\windows\path\", OsPathKind.Windows)]
|
||||
[TestCase(@"\relative\windows\path", OsPathKind.Windows)]
|
||||
[TestCase(@"relative\windows\path\", OsPathKind.Windows)]
|
||||
[TestCase(@"relative\windows\path", OsPathKind.Windows)]
|
||||
[TestCase(@"relative\", OsPathKind.Windows)]
|
||||
[TestCase(@"relative", OsPathKind.Unknown)]
|
||||
[TestCase("/rooted/linux/path/", OsPathKind.Unix)]
|
||||
[TestCase("/rooted/linux/path", OsPathKind.Unix)]
|
||||
[TestCase("/", OsPathKind.Unix)]
|
||||
[TestCase("linux/path", OsPathKind.Unix)]
|
||||
public void should_auto_detect_kind(String path, OsPathKind kind)
|
||||
{
|
||||
var result = new OsPath(path);
|
||||
|
||||
result.Kind.Should().Be(kind);
|
||||
|
||||
if (kind == OsPathKind.Windows)
|
||||
{
|
||||
result.IsWindowsPath.Should().BeTrue();
|
||||
result.IsUnixPath.Should().BeFalse();
|
||||
}
|
||||
else if (kind == OsPathKind.Unix)
|
||||
{
|
||||
result.IsWindowsPath.Should().BeFalse();
|
||||
result.IsUnixPath.Should().BeTrue();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.IsWindowsPath.Should().BeFalse();
|
||||
result.IsUnixPath.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_directory_slash()
|
||||
{
|
||||
var osPath = new OsPath(@"C:\rooted\windows\path\");
|
||||
|
||||
osPath.Directory.Should().NotBeNull();
|
||||
osPath.Directory.ToString().Should().Be(@"C:\rooted\windows\");
|
||||
}
|
||||
|
||||
[TestCase(@"C:\rooted\windows\path", @"C:\rooted\windows\")]
|
||||
[TestCase(@"C:\rooted", @"C:\")]
|
||||
[TestCase(@"C:", null)]
|
||||
[TestCase("/rooted/linux/path", "/rooted/linux/")]
|
||||
[TestCase("/rooted", "/")]
|
||||
[TestCase("/", null)]
|
||||
public void should_return_parent_directory(String path, String expectedParent)
|
||||
{
|
||||
var osPath = new OsPath(path);
|
||||
|
||||
osPath.Directory.Should().NotBeNull();
|
||||
osPath.Directory.Should().Be(new OsPath(expectedParent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_empty_as_parent_of_root_unc()
|
||||
{
|
||||
var osPath = new OsPath(@"\\unc");
|
||||
|
||||
osPath.Directory.IsEmpty.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase(@"C:\rooted\windows\path")]
|
||||
[TestCase(@"C:")]
|
||||
[TestCase(@"\\blaat")]
|
||||
[TestCase("/rooted/linux/path")]
|
||||
[TestCase("/")]
|
||||
public void should_detect_rooted_ospaths(String path)
|
||||
{
|
||||
var osPath = new OsPath(path);
|
||||
|
||||
osPath.IsRooted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase(@"\rooted\windows\path")]
|
||||
[TestCase(@"rooted\windows\path")]
|
||||
[TestCase(@"path")]
|
||||
[TestCase("linux/path")]
|
||||
public void should_detect_unrooted_ospaths(String path)
|
||||
{
|
||||
var osPath = new OsPath(path);
|
||||
|
||||
osPath.IsRooted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase(@"C:\rooted\windows\path", "path")]
|
||||
[TestCase(@"C:", "C:")]
|
||||
[TestCase(@"\\blaat", "blaat")]
|
||||
[TestCase("/rooted/linux/path", "path")]
|
||||
[TestCase("/", null)]
|
||||
[TestCase(@"\rooted\windows\path\", "path")]
|
||||
[TestCase(@"rooted\windows\path", "path")]
|
||||
[TestCase(@"path", "path")]
|
||||
[TestCase("linux/path", "path")]
|
||||
public void should_return_filename(String path, String expectedFilePath)
|
||||
{
|
||||
var osPath = new OsPath(path);
|
||||
|
||||
osPath.FileName.Should().Be(expectedFilePath);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_compare_windows_ospathkind_case_insensitive()
|
||||
{
|
||||
var left = new OsPath(@"C:\rooted\Windows\path");
|
||||
var right = new OsPath(@"C:\rooted\windows\path");
|
||||
|
||||
left.Should().Be(right);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_compare_unix_ospathkind_case_sensitive()
|
||||
{
|
||||
var left = new OsPath(@"/rooted/Linux/path");
|
||||
var right = new OsPath(@"/rooted/linux/path");
|
||||
|
||||
left.Should().NotBe(right);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_ignore_trailing_slash_during_compare()
|
||||
{
|
||||
var left = new OsPath(@"/rooted/linux/path/");
|
||||
var right = new OsPath(@"/rooted/linux/path");
|
||||
|
||||
left.Should().NotBe(right);
|
||||
}
|
||||
|
||||
[TestCase(@"C:\Test", @"sub", @"C:\Test\sub")]
|
||||
[TestCase(@"C:\Test", @"sub\test", @"C:\Test\sub\test")]
|
||||
[TestCase(@"C:\Test\", @"\sub", @"C:\Test\sub")]
|
||||
[TestCase(@"C:\Test", @"sub\", @"C:\Test\sub\")]
|
||||
[TestCase(@"C:\Test", @"C:\Test2\sub", @"C:\Test2\sub")]
|
||||
[TestCase(@"/Test", @"sub", @"/Test/sub")]
|
||||
[TestCase(@"/Test", @"sub/", @"/Test/sub/")]
|
||||
[TestCase(@"/Test", @"sub/", @"/Test/sub/")]
|
||||
[TestCase(@"/Test/", @"sub/test/", @"/Test/sub/test/")]
|
||||
[TestCase(@"/Test/", @"/Test2/", @"/Test2/")]
|
||||
[TestCase(@"C:\Test", "", @"C:\Test")]
|
||||
public void should_combine_path(String left, String right, String expectedResult)
|
||||
{
|
||||
var osPathLeft = new OsPath(left);
|
||||
var osPathRight = new OsPath(right);
|
||||
|
||||
var result = osPathLeft + osPathRight;
|
||||
|
||||
result.FullPath.Should().Be(expectedResult);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fix_slashes_windows()
|
||||
{
|
||||
var osPath = new OsPath(@"C:/on/windows/transmission\uses/forward/slashes");
|
||||
|
||||
osPath.Kind.Should().Be(OsPathKind.Windows);
|
||||
osPath.FullPath.Should().Be(@"C:\on\windows\transmission\uses\forward\slashes");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fix_slashes_unix()
|
||||
{
|
||||
var osPath = new OsPath(@"/just/a/test\to\verify the/slashes\");
|
||||
|
||||
osPath.Kind.Should().Be(OsPathKind.Unix);
|
||||
osPath.FullPath.Should().Be(@"/just/a/test/to/verify the/slashes/");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_combine_mixed_slashes()
|
||||
{
|
||||
var left = new OsPath(@"C:/on/windows/transmission");
|
||||
var right = new OsPath(@"uses/forward/slashes", OsPathKind.Unknown);
|
||||
|
||||
var osPath = left + right;
|
||||
|
||||
osPath.Kind.Should().Be(OsPathKind.Windows);
|
||||
osPath.FullPath.Should().Be(@"C:\on\windows\transmission\uses\forward\slashes");
|
||||
}
|
||||
|
||||
[TestCase(@"C:\Test\Data\", @"C:\Test\Data\Sub\Folder", @"Sub\Folder")]
|
||||
[TestCase(@"C:\Test\Data\", @"C:\Test\Data2\Sub\Folder", @"..\Data2\Sub\Folder")]
|
||||
[TestCase(@"/parent/folder", @"/parent/folder/Sub/Folder", @"Sub/Folder")]
|
||||
public void should_create_relative_path(String parent, String child, String expected)
|
||||
{
|
||||
var left = new OsPath(child);
|
||||
var right = new OsPath(parent);
|
||||
|
||||
var osPath = left - right;
|
||||
|
||||
osPath.Kind.Should().Be(OsPathKind.Unknown);
|
||||
osPath.FullPath.Should().Be(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_null_as_empty()
|
||||
{
|
||||
var result = new OsPath(null);
|
||||
|
||||
result.FullPath.Should().BeEmpty();
|
||||
result.IsEmpty.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase(@"C:\Test\", @"C:\Test", true)]
|
||||
[TestCase(@"C:\Test\", @"C:\Test\Contains\", true)]
|
||||
[TestCase(@"C:\Test\", @"C:\Other\", false)]
|
||||
public void should_evaluate_contains(String parent, String child, Boolean expectedResult)
|
||||
{
|
||||
var left = new OsPath(parent);
|
||||
var right = new OsPath(child);
|
||||
|
||||
var result = left.Contains(right);
|
||||
|
||||
result.Should().Be(expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,406 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Common.Disk
|
||||
{
|
||||
public struct OsPath : IEquatable<OsPath>
|
||||
{
|
||||
private readonly String _path;
|
||||
private readonly OsPathKind _kind;
|
||||
|
||||
public OsPath(String path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
_kind = OsPathKind.Unknown;
|
||||
_path = String.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
_kind = DetectPathKind(path);
|
||||
_path = FixSlashes(path, _kind);
|
||||
}
|
||||
}
|
||||
|
||||
public OsPath(String path, OsPathKind kind)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
_kind = kind;
|
||||
_path = String.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
_kind = kind;
|
||||
_path = FixSlashes(path, kind);
|
||||
}
|
||||
}
|
||||
|
||||
private static OsPathKind DetectPathKind(String path)
|
||||
{
|
||||
if (path.StartsWith("/"))
|
||||
{
|
||||
return OsPathKind.Unix;
|
||||
}
|
||||
if (path.Contains(':') || path.Contains('\\'))
|
||||
{
|
||||
return OsPathKind.Windows;
|
||||
}
|
||||
else if (path.Contains('/'))
|
||||
{
|
||||
return OsPathKind.Unix;
|
||||
}
|
||||
else
|
||||
{
|
||||
return OsPathKind.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
private static String FixSlashes(String path, OsPathKind kind)
|
||||
{
|
||||
if (kind == OsPathKind.Windows)
|
||||
{
|
||||
return path.Replace('/', '\\');
|
||||
}
|
||||
else if (kind == OsPathKind.Unix)
|
||||
{
|
||||
return path.Replace('\\', '/');
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public OsPathKind Kind
|
||||
{
|
||||
get { return _kind; }
|
||||
}
|
||||
|
||||
public Boolean IsWindowsPath
|
||||
{
|
||||
get { return _kind == OsPathKind.Windows; }
|
||||
}
|
||||
|
||||
public Boolean IsUnixPath
|
||||
{
|
||||
get { return _kind == OsPathKind.Unix; }
|
||||
}
|
||||
|
||||
public Boolean IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return _path.IsNullOrWhiteSpace();
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean IsRooted
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsWindowsPath)
|
||||
{
|
||||
return _path.StartsWith(@"\\") || _path.Contains(':');
|
||||
}
|
||||
else if (IsUnixPath)
|
||||
{
|
||||
return _path.StartsWith("/");
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OsPath Directory
|
||||
{
|
||||
get
|
||||
{
|
||||
var index = GetFileNameIndex();
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
return new OsPath(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OsPath(_path.Substring(0, index), _kind).AsDirectory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String FullPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
|
||||
public String FileName
|
||||
{
|
||||
get
|
||||
{
|
||||
var index = GetFileNameIndex();
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
var path = _path.Trim('\\', '/');
|
||||
|
||||
if (path.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _path.Substring(index).Trim('\\', '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Int32 GetFileNameIndex()
|
||||
{
|
||||
if (_path.Length < 2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var index = _path.LastIndexOfAny(new[] { '/', '\\' }, _path.Length - 2);
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_path.StartsWith(@"\\") && index < 2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_path.StartsWith("/") && index == 0)
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private String[] GetFragments()
|
||||
{
|
||||
return _path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
public override Int32 GetHashCode()
|
||||
{
|
||||
return _path.ToLowerInvariant().GetHashCode();
|
||||
}
|
||||
|
||||
public override Boolean Equals(Object obj)
|
||||
{
|
||||
if (obj is OsPath)
|
||||
{
|
||||
return Equals((OsPath)obj);
|
||||
}
|
||||
else if (obj is String)
|
||||
{
|
||||
return Equals(new OsPath(obj as String));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public OsPath AsDirectory()
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
if (Kind == OsPathKind.Windows)
|
||||
{
|
||||
return new OsPath(_path.TrimEnd('\\') + "\\", _kind);
|
||||
}
|
||||
else if (Kind == OsPathKind.Unix)
|
||||
{
|
||||
return new OsPath(_path.TrimEnd('/') + "/", _kind);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean Contains(OsPath other)
|
||||
{
|
||||
if (!IsRooted || !other.IsRooted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var leftFragments = GetFragments();
|
||||
var rightFragments = other.GetFragments();
|
||||
|
||||
if (rightFragments.Length < leftFragments.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var stringComparison = (Kind == OsPathKind.Windows || other.Kind == OsPathKind.Windows) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture;
|
||||
|
||||
for (int i = 0; i < leftFragments.Length; i++)
|
||||
{
|
||||
if (!String.Equals(leftFragments[i], rightFragments[i], stringComparison))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Boolean Equals(OsPath other)
|
||||
{
|
||||
if (ReferenceEquals(other, null)) return false;
|
||||
|
||||
if (_path == other._path)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var left = _path;
|
||||
var right = other._path;
|
||||
|
||||
if (Kind == OsPathKind.Windows || other.Kind == OsPathKind.Windows)
|
||||
{
|
||||
return String.Equals(left, right, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.Equals(left, right, StringComparison.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static Boolean operator ==(OsPath left, OsPath right)
|
||||
{
|
||||
if (ReferenceEquals(left, null)) return ReferenceEquals(right, null);
|
||||
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static Boolean operator !=(OsPath left, OsPath right)
|
||||
{
|
||||
if (ReferenceEquals(left, null)) return !ReferenceEquals(right, null);
|
||||
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static OsPath operator +(OsPath left, OsPath right)
|
||||
{
|
||||
if (left.Kind != right.Kind && right.Kind != OsPathKind.Unknown)
|
||||
{
|
||||
throw new Exception(String.Format("Cannot combine OsPaths of different platforms ('{0}' + '{1}')", left, right));
|
||||
}
|
||||
|
||||
if (right.IsEmpty)
|
||||
{
|
||||
return left;
|
||||
}
|
||||
|
||||
if (right.IsRooted)
|
||||
{
|
||||
return right;
|
||||
}
|
||||
|
||||
if (left.Kind == OsPathKind.Windows || right.Kind == OsPathKind.Windows)
|
||||
{
|
||||
return new OsPath(String.Join("\\", left._path.TrimEnd('\\'), right._path.TrimStart('\\')), OsPathKind.Windows);
|
||||
}
|
||||
else if (left.Kind == OsPathKind.Unix || right.Kind == OsPathKind.Unix)
|
||||
{
|
||||
return new OsPath(String.Join("/", left._path.TrimEnd('/'), right._path), OsPathKind.Unix);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OsPath(String.Join("/", left._path, right._path), OsPathKind.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
public static OsPath operator +(OsPath left, String right)
|
||||
{
|
||||
return left + new OsPath(right);
|
||||
}
|
||||
|
||||
public static OsPath operator -(OsPath left, OsPath right)
|
||||
{
|
||||
if (!left.IsRooted || !right.IsRooted)
|
||||
{
|
||||
throw new ArgumentException("Cannot determine relative path for unrooted paths.");
|
||||
}
|
||||
|
||||
var leftFragments = left.GetFragments();
|
||||
var rightFragments = right.GetFragments();
|
||||
|
||||
var stringComparison = (left.Kind == OsPathKind.Windows || right.Kind == OsPathKind.Windows) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < leftFragments.Length && i < rightFragments.Length; i++)
|
||||
{
|
||||
if (!String.Equals(leftFragments[i], rightFragments[i], stringComparison))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
return right;
|
||||
}
|
||||
|
||||
var newFragments = new List<String>();
|
||||
|
||||
for (int j = i; j < rightFragments.Length; j++)
|
||||
{
|
||||
newFragments.Add("..");
|
||||
}
|
||||
|
||||
for (int j = i; j < leftFragments.Length; j++)
|
||||
{
|
||||
newFragments.Add(leftFragments[j]);
|
||||
}
|
||||
|
||||
if (left.FullPath.EndsWith("\\") || left.FullPath.EndsWith("/"))
|
||||
{
|
||||
newFragments.Add(String.Empty);
|
||||
}
|
||||
|
||||
if (left.Kind == OsPathKind.Windows || right.Kind == OsPathKind.Windows)
|
||||
{
|
||||
return new OsPath(String.Join("\\", newFragments), OsPathKind.Unknown);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OsPath(String.Join("/", newFragments), OsPathKind.Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum OsPathKind
|
||||
{
|
||||
Unknown,
|
||||
Windows,
|
||||
Unix
|
||||
}
|
||||
}
|
|
@ -71,6 +71,7 @@
|
|||
<Compile Include="ConvertBase32.cs" />
|
||||
<Compile Include="Crypto\HashProvider.cs" />
|
||||
<Compile Include="DictionaryExtensions.cs" />
|
||||
<Compile Include="Disk\OsPath.cs" />
|
||||
<Compile Include="Disk\DiskProviderBase.cs" />
|
||||
<Compile Include="Disk\IDiskProvider.cs" />
|
||||
<Compile Include="Disk\TransferMode.cs" />
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.Download
|
|||
_completed = Builder<DownloadClientItem>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(h => h.Status = DownloadItemStatus.Completed)
|
||||
.With(h => h.OutputPath = @"C:\DropFolder\MyDownload".AsOsAgnostic())
|
||||
.With(h => h.OutputPath = new OsPath(@"C:\DropFolder\MyDownload".AsOsAgnostic()))
|
||||
.With(h => h.Title = "Drone.S01E01.HDTV")
|
||||
.Build()
|
||||
.ToList();
|
||||
|
@ -325,7 +325,7 @@ namespace NzbDrone.Core.Test.Download
|
|||
_completed.AddRange(Builder<DownloadClientItem>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(h => h.Status = DownloadItemStatus.Completed)
|
||||
.With(h => h.OutputPath = @"C:\DropFolder\MyDownload".AsOsAgnostic())
|
||||
.With(h => h.OutputPath = new OsPath(@"C:\DropFolder\MyDownload".AsOsAgnostic()))
|
||||
.With(h => h.Title = "Drone.S01E01.HDTV")
|
||||
.Build());
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
|||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new Byte[0]));
|
||||
|
||||
Mocker.GetMock<IRemotePathMappingService>()
|
||||
.Setup(v => v.RemapRemoteToLocal(It.IsAny<String>(), It.IsAny<String>()))
|
||||
.Returns<String, String>((h,r) => r);
|
||||
.Setup(v => v.RemapRemoteToLocal(It.IsAny<String>(), It.IsAny<OsPath>()))
|
||||
.Returns<String, OsPath>((h, r) => r);
|
||||
}
|
||||
|
||||
protected virtual RemoteEpisode CreateRemoteEpisode()
|
||||
|
|
|
@ -14,6 +14,7 @@ using NzbDrone.Core.Parser.Model;
|
|||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.RemotePathMappings;
|
||||
using NzbDrone.Common.Disk;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
||||
{
|
||||
|
@ -300,8 +301,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
|||
public void should_return_status_with_mounted_outputdir()
|
||||
{
|
||||
Mocker.GetMock<IRemotePathMappingService>()
|
||||
.Setup(v => v.RemapRemoteToLocal("127.0.0.1", "/remote/mount/tv"))
|
||||
.Returns(@"O:\mymount".AsOsAgnostic());
|
||||
.Setup(v => v.RemapRemoteToLocal("127.0.0.1", It.IsAny<OsPath>()))
|
||||
.Returns(new OsPath(@"O:\mymount".AsOsAgnostic()));
|
||||
|
||||
var result = Subject.GetStatus();
|
||||
|
||||
|
@ -314,8 +315,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
|||
public void should_remap_storage_if_mounted()
|
||||
{
|
||||
Mocker.GetMock<IRemotePathMappingService>()
|
||||
.Setup(v => v.RemapRemoteToLocal("127.0.0.1", "/remote/mount/tv/Droned.S01E01.Pilot.1080p.WEB-DL-DRONE"))
|
||||
.Returns(@"O:\mymount\Droned.S01E01.Pilot.1080p.WEB-DL-DRONE".AsOsAgnostic());
|
||||
.Setup(v => v.RemapRemoteToLocal("127.0.0.1", It.IsAny<OsPath>()))
|
||||
.Returns(new OsPath(@"O:\mymount\Droned.S01E01.Pilot.1080p.WEB-DL-DRONE".AsOsAgnostic()));
|
||||
|
||||
GivenQueue(null);
|
||||
GivenHistory(_completed);
|
||||
|
|
|
@ -11,6 +11,7 @@ using NzbDrone.Core.Download.Clients.Sabnzbd.Responses;
|
|||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Core.RemotePathMappings;
|
||||
using NzbDrone.Common.Disk;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
{
|
||||
|
@ -303,15 +304,15 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
|||
|
||||
var result = Subject.GetItems().Single();
|
||||
|
||||
result.OutputPath.Should().Be((@"C:\sorted\" + title).AsOsAgnostic());
|
||||
result.OutputPath.Should().Be(new OsPath((@"C:\sorted\" + title).AsOsAgnostic()).AsDirectory());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_remap_storage_if_mounted()
|
||||
{
|
||||
Mocker.GetMock<IRemotePathMappingService>()
|
||||
.Setup(v => v.RemapRemoteToLocal("127.0.0.1", "/remote/mount/vv/Droned.S01E01.Pilot.1080p.WEB-DL-DRONE"))
|
||||
.Returns(@"O:\mymount\Droned.S01E01.Pilot.1080p.WEB-DL-DRONE".AsOsAgnostic());
|
||||
.Setup(v => v.RemapRemoteToLocal("127.0.0.1", It.IsAny<OsPath>()))
|
||||
.Returns(new OsPath(@"O:\mymount\Droned.S01E01.Pilot.1080p.WEB-DL-DRONE".AsOsAgnostic()));
|
||||
|
||||
GivenQueue(null);
|
||||
GivenHistory(_completed);
|
||||
|
@ -370,8 +371,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
|||
public void should_return_status_with_mounted_outputdir()
|
||||
{
|
||||
Mocker.GetMock<IRemotePathMappingService>()
|
||||
.Setup(v => v.RemapRemoteToLocal("127.0.0.1", "/remote/mount/vv"))
|
||||
.Returns(@"O:\mymount".AsOsAgnostic());
|
||||
.Setup(v => v.RemapRemoteToLocal("127.0.0.1", It.IsAny<OsPath>()))
|
||||
.Returns(new OsPath(@"O:\mymount".AsOsAgnostic()));
|
||||
|
||||
GivenQueue(null);
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
|||
.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())
|
||||
.With(v => v.DownloadItem.OutputPath = new OsPath(@"C:\Test\DropFolder\myfile.mkv".AsOsAgnostic()))
|
||||
.Build();
|
||||
|
||||
Mocker.GetMock<IDownloadTrackingService>()
|
||||
|
@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
|||
GivenCompletedDownloadHandling(true);
|
||||
GivenDroneFactoryFolder(true);
|
||||
|
||||
_completed.First().DownloadItem.OutputPath = (DRONE_FACTORY_FOLDER + @"\myfile.mkv").AsOsAgnostic();
|
||||
_completed.First().DownloadItem.OutputPath = new OsPath((DRONE_FACTORY_FOLDER + @"\myfile.mkv").AsOsAgnostic());
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
|
|
@ -97,13 +97,13 @@ namespace NzbDrone.Core.Test.RemotePathMappingsTests
|
|||
|
||||
GivenMapping();
|
||||
|
||||
var result = Subject.RemapRemoteToLocal(host, remotePath);
|
||||
var result = Subject.RemapRemoteToLocal(host, new OsPath(remotePath));
|
||||
|
||||
result.Should().Be(expectedLocalPath);
|
||||
}
|
||||
|
||||
[TestCase("my-server.localdomain", "/mnt/storage/downloads/tv", @"D:\mountedstorage\downloads\tv")]
|
||||
[TestCase("my-server.localdomain", "/mnt/storage", @"D:\mountedstorage")]
|
||||
[TestCase("my-server.localdomain", "/mnt/storage/", @"D:\mountedstorage")]
|
||||
[TestCase("my-2server.localdomain", "/mnt/storage/downloads/tv", "/mnt/storage/downloads/tv")]
|
||||
[TestCase("my-server.localdomain", "/mnt/storageabc/downloads/tv", "/mnt/storageabc/downloads/tv")]
|
||||
public void should_remap_local_to_remote(String host, String expectedRemotePath, String localPath)
|
||||
|
@ -112,7 +112,7 @@ namespace NzbDrone.Core.Test.RemotePathMappingsTests
|
|||
|
||||
GivenMapping();
|
||||
|
||||
var result = Subject.RemapLocalToRemote(host, localPath);
|
||||
var result = Subject.RemapLocalToRemote(host, new OsPath(localPath));
|
||||
|
||||
result.Should().Be(expectedRemotePath);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Disk;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class OsPathConverter : IConverter
|
||||
{
|
||||
public Object FromDB(ConverterContext context)
|
||||
{
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return DBNull.Value;
|
||||
}
|
||||
|
||||
var value = (String)context.DbValue;
|
||||
|
||||
return new OsPath(value);
|
||||
}
|
||||
|
||||
public Object FromDB(ColumnMap map, Object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public Object ToDB(Object clrValue)
|
||||
{
|
||||
var value = (OsPath)clrValue;
|
||||
|
||||
return value.FullPath;
|
||||
}
|
||||
|
||||
public Type DbType
|
||||
{
|
||||
get { return typeof(String); }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ using NzbDrone.Core.SeriesStats;
|
|||
using NzbDrone.Core.Tags;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Common.Disk;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
|
@ -114,6 +115,7 @@ namespace NzbDrone.Core.Datastore
|
|||
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedEpisodeInfo), new EmbeddedDocumentConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(ReleaseInfo), new EmbeddedDocumentConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(HashSet<Int32>), new EmbeddedDocumentConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(OsPath), new OsPathConverter());
|
||||
}
|
||||
|
||||
private static void RegisterProviderSettingConverter()
|
||||
|
|
|
@ -115,8 +115,8 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
|||
item.DownloadClient = Definition.Name;
|
||||
item.DownloadTime = TimeSpan.FromSeconds(torrent.SecondsDownloading);
|
||||
|
||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, torrent.DownloadPath);
|
||||
item.OutputPath = Path.Combine(outputPath, torrent.Name);
|
||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.DownloadPath));
|
||||
item.OutputPath = outputPath + torrent.Name;
|
||||
item.RemainingSize = torrent.Size - torrent.BytesDownloaded;
|
||||
item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta);
|
||||
item.TotalSize = torrent.Size;
|
||||
|
@ -172,11 +172,11 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
|||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
|
||||
var destDir = config.GetValueOrDefault("download_location") as string;
|
||||
var destDir = new OsPath(config.GetValueOrDefault("download_location") as string);
|
||||
|
||||
if (config.GetValueOrDefault("move_completed", false).ToString() == "True")
|
||||
{
|
||||
destDir = config.GetValueOrDefault("move_completed_path") as string;
|
||||
destDir = new OsPath(config.GetValueOrDefault("move_completed_path") as string);
|
||||
}
|
||||
|
||||
var status = new DownloadClientStatus
|
||||
|
@ -184,9 +184,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
|||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||
};
|
||||
|
||||
if (destDir != null)
|
||||
if (!destDir.IsEmpty)
|
||||
{
|
||||
status.OutputRootFolders = new List<string> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) };
|
||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) };
|
||||
}
|
||||
|
||||
return status;
|
||||
|
|
|
@ -131,7 +131,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
|||
historyItem.DownloadClientId = droneParameter == null ? item.Id.ToString() : droneParameter.Value.ToString();
|
||||
historyItem.Title = item.Name;
|
||||
historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo);
|
||||
historyItem.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, item.DestDir);
|
||||
historyItem.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(item.DestDir));
|
||||
historyItem.Category = item.Category;
|
||||
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;
|
||||
|
@ -215,7 +215,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
|||
|
||||
if (category != null)
|
||||
{
|
||||
status.OutputRootFolders = new List<String> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, category.DestDir) };
|
||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(category.DestDir)) };
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -321,10 +321,10 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
|||
|
||||
if (category != null)
|
||||
{
|
||||
var localPath = Settings.TvCategoryLocalPath;
|
||||
var localPath = new OsPath(Settings.TvCategoryLocalPath);
|
||||
Settings.TvCategoryLocalPath = null;
|
||||
|
||||
_remotePathMappingService.MigrateLocalCategoryPath(Definition.Id, Settings, Settings.Host, category.DestDir, localPath);
|
||||
_remotePathMappingService.MigrateLocalCategoryPath(Definition.Id, Settings, Settings.Host, new OsPath(category.DestDir), localPath);
|
||||
|
||||
_logger.Info("Discovered Local Category Path for {0}, the setting was automatically moved to the Remote Path Mapping table.", Definition.Name);
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
|||
|
||||
TotalSize = _diskProvider.GetFileSize(file),
|
||||
|
||||
OutputPath = file
|
||||
OutputPath = new OsPath(file)
|
||||
};
|
||||
|
||||
if (_diskProvider.IsFileLocked(file))
|
||||
|
|
|
@ -156,20 +156,20 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
|||
historyItem.Status = DownloadItemStatus.Downloading;
|
||||
}
|
||||
|
||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, sabHistoryItem.Storage);
|
||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(sabHistoryItem.Storage));
|
||||
|
||||
if (!outputPath.IsNullOrWhiteSpace())
|
||||
if (!outputPath.IsEmpty)
|
||||
{
|
||||
historyItem.OutputPath = outputPath;
|
||||
|
||||
var parent = outputPath.GetParentPath();
|
||||
while (parent != null)
|
||||
var parent = outputPath.Directory;
|
||||
while (!parent.IsEmpty)
|
||||
{
|
||||
if (Path.GetFileName(parent) == sabHistoryItem.Title)
|
||||
if (parent.FileName == sabHistoryItem.Title)
|
||||
{
|
||||
historyItem.OutputPath = parent;
|
||||
}
|
||||
parent = parent.GetParentPath();
|
||||
parent = parent.Directory;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,52 +259,21 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
|||
|
||||
protected IEnumerable<SabnzbdCategory> GetCategories(SabnzbdConfig config)
|
||||
{
|
||||
var completeDir = config.Misc.complete_dir.TrimEnd('\\', '/');
|
||||
var completeDir = new OsPath(config.Misc.complete_dir);
|
||||
|
||||
if (!completeDir.StartsWith("/") && !completeDir.StartsWith("\\") && !completeDir.Contains(':'))
|
||||
if (!completeDir.IsRooted)
|
||||
{
|
||||
var queue = _proxy.GetQueue(0, 1, Settings);
|
||||
var defaultRootFolder = new OsPath(queue.DefaultRootFolder);
|
||||
|
||||
if (queue.DefaultRootFolder.StartsWith("/"))
|
||||
{
|
||||
completeDir = queue.DefaultRootFolder + "/" + completeDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
completeDir = queue.DefaultRootFolder + "\\" + completeDir;
|
||||
}
|
||||
completeDir = defaultRootFolder + completeDir;
|
||||
}
|
||||
|
||||
foreach (var category in config.Categories)
|
||||
{
|
||||
var relativeDir = category.Dir.TrimEnd('*');
|
||||
var relativeDir = new OsPath(category.Dir.TrimEnd('*'));
|
||||
|
||||
if (relativeDir.IsNullOrWhiteSpace())
|
||||
{
|
||||
category.FullPath = completeDir;
|
||||
}
|
||||
else if (completeDir.StartsWith("/"))
|
||||
{ // Process remote Linux paths irrespective of our own OS.
|
||||
if (relativeDir.StartsWith("/"))
|
||||
{
|
||||
category.FullPath = relativeDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
category.FullPath = completeDir + "/" + relativeDir;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Process remote Windows paths irrespective of our own OS.
|
||||
if (relativeDir.StartsWith("\\") || relativeDir.Contains(':'))
|
||||
{
|
||||
category.FullPath = relativeDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
category.FullPath = completeDir + "\\" + relativeDir;
|
||||
}
|
||||
}
|
||||
category.FullPath = completeDir + relativeDir;
|
||||
|
||||
yield return category;
|
||||
}
|
||||
|
@ -329,7 +298,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
|||
|
||||
if (category != null)
|
||||
{
|
||||
status.OutputRootFolders = new List<String> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, category.FullPath) };
|
||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, category.FullPath) };
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -454,7 +423,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
|||
|
||||
if (category != null)
|
||||
{
|
||||
var localPath = Settings.TvCategoryLocalPath;
|
||||
var localPath = new OsPath(Settings.TvCategoryLocalPath);
|
||||
Settings.TvCategoryLocalPath = null;
|
||||
|
||||
_remotePathMappingService.MigrateLocalCategoryPath(Definition.Id, Settings, Settings.Host, category.FullPath, localPath);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Common.Disk;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
{
|
||||
|
@ -30,6 +31,6 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
|||
public String Script { get; set; }
|
||||
public String Dir { get; set; }
|
||||
|
||||
public String FullPath { get; set; }
|
||||
public OsPath FullPath { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ namespace NzbDrone.Core.Download.Clients.TorrentBlackhole
|
|||
|
||||
TotalSize = files.Select(_diskProvider.GetFileSize).Sum(),
|
||||
|
||||
OutputPath = folder
|
||||
OutputPath = new OsPath(folder)
|
||||
};
|
||||
|
||||
if (files.Any(_diskProvider.IsFileLocked))
|
||||
|
@ -108,7 +108,7 @@ namespace NzbDrone.Core.Download.Clients.TorrentBlackhole
|
|||
|
||||
TotalSize = _diskProvider.GetFileSize(videoFile),
|
||||
|
||||
OutputPath = videoFile
|
||||
OutputPath = new OsPath(videoFile)
|
||||
};
|
||||
|
||||
if (_diskProvider.IsFileLocked(videoFile))
|
||||
|
@ -140,7 +140,7 @@ namespace NzbDrone.Core.Download.Clients.TorrentBlackhole
|
|||
return new DownloadClientStatus
|
||||
{
|
||||
IsLocalhost = true,
|
||||
OutputRootFolders = new List<string> { Settings.WatchFolder }
|
||||
OutputRootFolders = new List<OsPath> { new OsPath(Settings.WatchFolder) }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -94,19 +94,11 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
|||
|
||||
foreach (var torrent in torrents)
|
||||
{
|
||||
var outputPath = torrent.DownloadDir;
|
||||
|
||||
// Transmission always returns path with forward slashes, even on windows.
|
||||
if (outputPath.IsNotNullOrWhiteSpace() && (outputPath.StartsWith(@"\\") || outputPath.Contains(':')))
|
||||
{
|
||||
outputPath = outputPath.Replace('/', '\\');
|
||||
}
|
||||
|
||||
outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, outputPath);
|
||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.DownloadDir));
|
||||
|
||||
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var directories = outputPath.Split('\\', '/');
|
||||
var directories = outputPath.FullPath.Split('\\', '/');
|
||||
if (!directories.Contains(String.Format(".{0}", Settings.TvCategory))) continue;
|
||||
}
|
||||
|
||||
|
@ -119,7 +111,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
|||
item.DownloadTime = TimeSpan.FromSeconds(torrent.SecondsDownloading);
|
||||
item.Message = torrent.ErrorString;
|
||||
|
||||
item.OutputPath = Path.Combine(outputPath, torrent.Name);
|
||||
item.OutputPath = outputPath + torrent.Name;
|
||||
item.RemainingSize = torrent.LeftUntilDone;
|
||||
item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta);
|
||||
item.TotalSize = torrent.TotalSize;
|
||||
|
@ -173,16 +165,10 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
|||
destDir = String.Format("{0}/.{1}", destDir, Settings.TvCategory);
|
||||
}
|
||||
|
||||
// Transmission always returns path with forward slashes, even on windows.
|
||||
if (destDir.StartsWith(@"\\") || destDir.Contains(':'))
|
||||
{
|
||||
destDir = destDir.Replace('/', '\\');
|
||||
}
|
||||
|
||||
return new DownloadClientStatus
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||
OutputRootFolders = new List<string> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) }
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(destDir)) }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace NzbDrone.Core.Download.Clients.UsenetBlackhole
|
|||
|
||||
TotalSize = files.Select(_diskProvider.GetFileSize).Sum(),
|
||||
|
||||
OutputPath = folder
|
||||
OutputPath = new OsPath(folder)
|
||||
};
|
||||
|
||||
if (files.Any(_diskProvider.IsFileLocked))
|
||||
|
@ -105,7 +105,7 @@ namespace NzbDrone.Core.Download.Clients.UsenetBlackhole
|
|||
|
||||
TotalSize = _diskProvider.GetFileSize(videoFile),
|
||||
|
||||
OutputPath = videoFile
|
||||
OutputPath = new OsPath(videoFile)
|
||||
};
|
||||
|
||||
if (_diskProvider.IsFileLocked(videoFile))
|
||||
|
@ -137,7 +137,7 @@ namespace NzbDrone.Core.Download.Clients.UsenetBlackhole
|
|||
return new DownloadClientStatus
|
||||
{
|
||||
IsLocalhost = true,
|
||||
OutputRootFolders = new List<string> { Settings.WatchFolder }
|
||||
OutputRootFolders = new List<OsPath> { new OsPath(Settings.WatchFolder) }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -102,19 +102,15 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
|||
item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta);
|
||||
}
|
||||
|
||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, torrent.RootDownloadPath);
|
||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.RootDownloadPath));
|
||||
|
||||
if (outputPath == null || Path.GetFileName(outputPath) == torrent.Name)
|
||||
if (outputPath == null || outputPath.FileName == torrent.Name)
|
||||
{
|
||||
item.OutputPath = outputPath;
|
||||
}
|
||||
else if (OsInfo.IsWindows && outputPath.EndsWith(":"))
|
||||
{
|
||||
item.OutputPath = Path.Combine(outputPath + Path.DirectorySeparatorChar, torrent.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.OutputPath = Path.Combine(outputPath, torrent.Name);
|
||||
item.OutputPath = outputPath + torrent.Name;
|
||||
}
|
||||
|
||||
if (torrent.Status.HasFlag(UTorrentTorrentStatus.Error))
|
||||
|
@ -162,20 +158,20 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
|||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
|
||||
String destDir = null;
|
||||
OsPath destDir = new OsPath(null);
|
||||
|
||||
if (config.GetValueOrDefault("dir_active_download_flag") == "true")
|
||||
{
|
||||
destDir = config.GetValueOrDefault("dir_active_download");
|
||||
destDir = new OsPath(config.GetValueOrDefault("dir_active_download"));
|
||||
}
|
||||
|
||||
if (config.GetValueOrDefault("dir_completed_download_flag") == "true")
|
||||
{
|
||||
destDir = config.GetValueOrDefault("dir_completed_download");
|
||||
destDir = new OsPath(config.GetValueOrDefault("dir_completed_download"));
|
||||
|
||||
if (config.GetValueOrDefault("dir_add_label") == "true")
|
||||
{
|
||||
destDir = Path.Combine(destDir, Settings.TvCategory);
|
||||
destDir = destDir + Settings.TvCategory;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,9 +180,9 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
|||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||
};
|
||||
|
||||
if (destDir != null)
|
||||
if (!destDir.IsEmpty)
|
||||
{
|
||||
status.OutputRootFolders = new List<String> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) };
|
||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) };
|
||||
}
|
||||
|
||||
return status;
|
||||
|
|
|
@ -79,16 +79,16 @@ namespace NzbDrone.Core.Download
|
|||
|
||||
else
|
||||
{
|
||||
var downloadedEpisodesFolder = _configService.DownloadedEpisodesFolder;
|
||||
var downloadedEpisodesFolder = new OsPath(_configService.DownloadedEpisodesFolder);
|
||||
var downloadItemOutputPath = trackedDownload.DownloadItem.OutputPath;
|
||||
|
||||
if (downloadItemOutputPath.IsNullOrWhiteSpace())
|
||||
if (downloadItemOutputPath.IsEmpty)
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Download doesn't contain intermediate path, ignoring download.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!downloadedEpisodesFolder.IsNullOrWhiteSpace() && (downloadedEpisodesFolder.PathEquals(downloadItemOutputPath) || downloadedEpisodesFolder.IsParentPath(downloadItemOutputPath)))
|
||||
if (!downloadedEpisodesFolder.IsEmpty && downloadedEpisodesFolder.Contains(downloadItemOutputPath))
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Intermediate Download path inside drone factory, ignoring download.");
|
||||
return;
|
||||
|
@ -113,7 +113,7 @@ namespace NzbDrone.Core.Download
|
|||
public List<ImportResult> Import(TrackedDownload trackedDownload, String overrideOutputPath = null)
|
||||
{
|
||||
var importResults = new List<ImportResult>();
|
||||
var outputPath = overrideOutputPath ?? trackedDownload.DownloadItem.OutputPath;
|
||||
var outputPath = overrideOutputPath ?? trackedDownload.DownloadItem.OutputPath.FullPath;
|
||||
|
||||
if (_diskProvider.FolderExists(outputPath))
|
||||
{
|
||||
|
@ -216,16 +216,16 @@ namespace NzbDrone.Core.Download
|
|||
_logger.Debug("[{0}] Removing completed download from history.", trackedDownload.DownloadItem.Title);
|
||||
downloadClient.RemoveItem(trackedDownload.DownloadItem.DownloadClientId);
|
||||
|
||||
if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath))
|
||||
if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath))
|
||||
{
|
||||
_logger.Debug("Removing completed download directory: {0}",
|
||||
trackedDownload.DownloadItem.OutputPath);
|
||||
_diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath, true);
|
||||
_diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath.FullPath, true);
|
||||
}
|
||||
else if (_diskProvider.FileExists(trackedDownload.DownloadItem.OutputPath))
|
||||
else if (_diskProvider.FileExists(trackedDownload.DownloadItem.OutputPath.FullPath))
|
||||
{
|
||||
_logger.Debug("Removing completed download file: {0}", trackedDownload.DownloadItem.OutputPath);
|
||||
_diskProvider.DeleteFile(trackedDownload.DownloadItem.OutputPath);
|
||||
_diskProvider.DeleteFile(trackedDownload.DownloadItem.OutputPath.FullPath);
|
||||
}
|
||||
|
||||
trackedDownload.State = TrackedDownloadState.Removed;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
|
@ -14,7 +16,7 @@ namespace NzbDrone.Core.Download
|
|||
public TimeSpan? DownloadTime { get; set; }
|
||||
public TimeSpan? RemainingTime { get; set; }
|
||||
|
||||
public String OutputPath { get; set; }
|
||||
public OsPath OutputPath { get; set; }
|
||||
public String Message { get; set; }
|
||||
|
||||
public DownloadItemStatus Status { get; set; }
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Common.Disk;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
public class DownloadClientStatus
|
||||
{
|
||||
public Boolean IsLocalhost { get; set; }
|
||||
public List<String> OutputRootFolders { get; set; }
|
||||
public List<OsPath> OutputRootFolders { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,12 +25,12 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
|||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var droneFactoryFolder = _configService.DownloadedEpisodesFolder;
|
||||
var droneFactoryFolder = new OsPath(_configService.DownloadedEpisodesFolder);
|
||||
var downloadClients = _provideDownloadClient.GetDownloadClients().Select(v => new { downloadClient = v, status = v.GetStatus() }).ToList();
|
||||
|
||||
var downloadClientIsLocalHost = downloadClients.All(v => v.status.IsLocalhost);
|
||||
var downloadClientOutputInDroneFactory = !droneFactoryFolder.IsNullOrWhiteSpace()
|
||||
&& downloadClients.Any(v => v.status.OutputRootFolders != null && v.status.OutputRootFolders.Contains(droneFactoryFolder, PathEqualityComparer.Instance));
|
||||
var downloadClientOutputInDroneFactory = !droneFactoryFolder.IsEmpty
|
||||
&& downloadClients.Any(v => v.status.OutputRootFolders != null && v.status.OutputRootFolders.Any(droneFactoryFolder.Contains));
|
||||
|
||||
if (!_configService.IsDefined("EnableCompletedDownloadHandling"))
|
||||
{
|
||||
|
@ -66,14 +66,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
|||
}
|
||||
}
|
||||
|
||||
if (!_configService.EnableCompletedDownloadHandling && droneFactoryFolder.IsNullOrWhiteSpace())
|
||||
if (!_configService.EnableCompletedDownloadHandling && droneFactoryFolder.IsEmpty)
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling or configure Drone factory");
|
||||
}
|
||||
|
||||
if (_configService.EnableCompletedDownloadHandling && !droneFactoryFolder.IsNullOrWhiteSpace())
|
||||
if (_configService.EnableCompletedDownloadHandling && !droneFactoryFolder.IsEmpty)
|
||||
{
|
||||
if (_downloadTrackingService.GetCompletedDownloads().Any(v => droneFactoryFolder.PathEquals(v.DownloadItem.OutputPath) || droneFactoryFolder.IsParentPath(v.DownloadItem.OutputPath)))
|
||||
if (_downloadTrackingService.GetCompletedDownloads().Any(v => droneFactoryFolder.Contains(v.DownloadItem.OutputPath)))
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Completed Download Handling conflict with Drone Factory (Conflicting History Item)", "Migrating-to-Completed-Download-Handling#conflicting-download-client-category");
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@
|
|||
<Compile Include="Datastore\Converters\EmbeddedDocumentConverter.cs" />
|
||||
<Compile Include="Datastore\Converters\EnumIntConverter.cs" />
|
||||
<Compile Include="Datastore\Converters\Int32Converter.cs" />
|
||||
<Compile Include="Datastore\Converters\OsPathConverter.cs" />
|
||||
<Compile Include="Datastore\Converters\ProviderSettingConverter.cs" />
|
||||
<Compile Include="Datastore\Converters\QualityIntConverter.cs" />
|
||||
<Compile Include="Datastore\Converters\UtcConverter.cs" />
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@ namespace NzbDrone.Core.RemotePathMappings
|
|||
RemotePathMapping Get(int id);
|
||||
RemotePathMapping Update(RemotePathMapping mapping);
|
||||
|
||||
String RemapRemoteToLocal(String host, String remotePath);
|
||||
String RemapLocalToRemote(String host, String localPath);
|
||||
OsPath RemapRemoteToLocal(String host, OsPath remotePath);
|
||||
OsPath RemapLocalToRemote(String host, OsPath localPath);
|
||||
|
||||
// TODO: Remove around January 2015. Used to migrate legacy Local Category Path settings.
|
||||
void MigrateLocalCategoryPath(Int32 downloadClientId, IProviderConfig newSettings, String host, String remotePath, String localPath);
|
||||
void MigrateLocalCategoryPath(Int32 downloadClientId, IProviderConfig newSettings, String host, OsPath remotePath, OsPath localPath);
|
||||
}
|
||||
|
||||
public class RemotePathMappingService : IRemotePathMappingService
|
||||
|
@ -60,8 +60,8 @@ namespace NzbDrone.Core.RemotePathMappings
|
|||
|
||||
public RemotePathMapping Add(RemotePathMapping mapping)
|
||||
{
|
||||
mapping.LocalPath = CleanPath(mapping.LocalPath);
|
||||
mapping.RemotePath = CleanPath(mapping.RemotePath);
|
||||
mapping.LocalPath = new OsPath(mapping.LocalPath).AsDirectory().FullPath;
|
||||
mapping.RemotePath = new OsPath(mapping.RemotePath).AsDirectory().FullPath;
|
||||
|
||||
var all = All();
|
||||
|
||||
|
@ -106,17 +106,20 @@ namespace NzbDrone.Core.RemotePathMappings
|
|||
throw new ArgumentException("Invalid Host");
|
||||
}
|
||||
|
||||
if (mapping.RemotePath.IsNullOrWhiteSpace())
|
||||
var remotePath = new OsPath(mapping.RemotePath);
|
||||
var localPath = new OsPath(mapping.LocalPath);
|
||||
|
||||
if (remotePath.IsEmpty)
|
||||
{
|
||||
throw new ArgumentException("Invalid RemotePath");
|
||||
}
|
||||
|
||||
if (mapping.LocalPath.IsNullOrWhiteSpace() || !Path.IsPathRooted(mapping.LocalPath))
|
||||
if (localPath.IsEmpty || !localPath.IsRooted)
|
||||
{
|
||||
throw new ArgumentException("Invalid LocalPath");
|
||||
}
|
||||
|
||||
if (!_diskProvider.FolderExists(mapping.LocalPath))
|
||||
if (!_diskProvider.FolderExists(localPath.FullPath))
|
||||
{
|
||||
throw new DirectoryNotFoundException("Can't add mount point directory that doesn't exist.");
|
||||
}
|
||||
|
@ -127,27 +130,18 @@ namespace NzbDrone.Core.RemotePathMappings
|
|||
}
|
||||
}
|
||||
|
||||
public String RemapRemoteToLocal(String host, String remotePath)
|
||||
public OsPath RemapRemoteToLocal(String host, OsPath remotePath)
|
||||
{
|
||||
if (remotePath.IsNullOrWhiteSpace())
|
||||
if (remotePath.IsEmpty)
|
||||
{
|
||||
return remotePath;
|
||||
}
|
||||
|
||||
var cleanRemotePath = CleanPath(remotePath);
|
||||
|
||||
foreach (var mapping in All())
|
||||
{
|
||||
if (host == mapping.Host && cleanRemotePath.StartsWith(mapping.RemotePath))
|
||||
if (host == mapping.Host && new OsPath(mapping.RemotePath).Contains(remotePath))
|
||||
{
|
||||
var localPath = mapping.LocalPath + cleanRemotePath.Substring(mapping.RemotePath.Length);
|
||||
|
||||
localPath = localPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
|
||||
if (!remotePath.EndsWith("/") && !remotePath.EndsWith("\\"))
|
||||
{
|
||||
localPath = localPath.TrimEnd('/', '\\');
|
||||
}
|
||||
var localPath = new OsPath(mapping.LocalPath) + (remotePath - new OsPath(mapping.RemotePath));
|
||||
|
||||
return localPath;
|
||||
}
|
||||
|
@ -156,29 +150,18 @@ namespace NzbDrone.Core.RemotePathMappings
|
|||
return remotePath;
|
||||
}
|
||||
|
||||
public String RemapLocalToRemote(String host, String localPath)
|
||||
public OsPath RemapLocalToRemote(String host, OsPath localPath)
|
||||
{
|
||||
if (localPath.IsNullOrWhiteSpace())
|
||||
if (localPath.IsEmpty)
|
||||
{
|
||||
return localPath;
|
||||
}
|
||||
|
||||
var cleanLocalPath = CleanPath(localPath);
|
||||
|
||||
foreach (var mapping in All())
|
||||
{
|
||||
if (host != mapping.Host) continue;
|
||||
|
||||
if (cleanLocalPath.StartsWith(mapping.LocalPath))
|
||||
if (host == mapping.Host && new OsPath(mapping.LocalPath).Contains(localPath))
|
||||
{
|
||||
var remotePath = mapping.RemotePath + cleanLocalPath.Substring(mapping.LocalPath.Length);
|
||||
|
||||
remotePath = remotePath.Replace(Path.DirectorySeparatorChar, mapping.RemotePath.Contains('\\') ? '\\' : '/');
|
||||
|
||||
if (!localPath.EndsWith("/") && !localPath.EndsWith("\\"))
|
||||
{
|
||||
remotePath = remotePath.TrimEnd('/', '\\');
|
||||
}
|
||||
var remotePath = new OsPath(mapping.RemotePath) + (localPath - new OsPath(mapping.LocalPath));
|
||||
|
||||
return remotePath;
|
||||
}
|
||||
|
@ -188,35 +171,20 @@ namespace NzbDrone.Core.RemotePathMappings
|
|||
}
|
||||
|
||||
// TODO: Remove around January 2015. Used to migrate legacy Local Category Path settings.
|
||||
public void MigrateLocalCategoryPath(Int32 downloadClientId, IProviderConfig newSettings, String host, String remotePath, String localPath)
|
||||
public void MigrateLocalCategoryPath(Int32 downloadClientId, IProviderConfig newSettings, String host, OsPath remotePath, OsPath localPath)
|
||||
{
|
||||
_logger.Debug("Migrating local category path for Host {0}/{1} to {2}", host, remotePath, localPath);
|
||||
|
||||
var existingMappings = All().Where(v => v.Host == host).ToList();
|
||||
|
||||
remotePath = CleanPath(remotePath);
|
||||
localPath = CleanPath(localPath);
|
||||
|
||||
if (!existingMappings.Any(v => v.LocalPath == localPath && v.RemotePath == remotePath))
|
||||
if (!existingMappings.Any(v => new OsPath(v.LocalPath) == localPath && new OsPath(v.RemotePath) == remotePath))
|
||||
{
|
||||
Add(new RemotePathMapping { Host = host, RemotePath = remotePath, LocalPath = localPath });
|
||||
Add(new RemotePathMapping { Host = host, RemotePath = remotePath.FullPath, LocalPath = localPath.FullPath });
|
||||
}
|
||||
|
||||
var downloadClient = _downloadClientRepository.Get(downloadClientId);
|
||||
downloadClient.Settings = newSettings;
|
||||
_downloadClientRepository.Update(downloadClient);
|
||||
}
|
||||
|
||||
private static String CleanPath(String path)
|
||||
{
|
||||
if (path.StartsWith(@"\\") || path.Contains(':'))
|
||||
{
|
||||
return path.TrimEnd('\\', '/').Replace('/', '\\') + "\\";
|
||||
}
|
||||
else
|
||||
{
|
||||
return path.TrimEnd('\\', '/').Replace('\\', '/') + "/";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue