Added OsPath to handle OS agnostic path handling.

pull/3113/head
Taloth Saldono 10 years ago
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…
Cancel
Save