Fixed: Updating movie path from different OS paths

(cherry picked from commit e791f4b743d9660b0ad1decc4c5ed0e864f3b243)

Closes #10218
pull/10296/head
Mark McDowall 4 months ago committed by Bogdan
parent 3a4446cc8e
commit 322df78f5a

@ -133,11 +133,16 @@ namespace NzbDrone.Common.Test
[TestCase(@"C:\test\", @"C:\Test\mydir")] [TestCase(@"C:\test\", @"C:\Test\mydir")]
[TestCase(@"C:\test", @"C:\Test\mydir\")] [TestCase(@"C:\test", @"C:\Test\mydir\")]
public void path_should_be_parent_on_windows_only(string parentPath, string childPath) public void windows_path_should_be_parent(string parentPath, string childPath)
{ {
var expectedResult = OsInfo.IsWindows; parentPath.IsParentPath(childPath).Should().Be(true);
}
parentPath.IsParentPath(childPath).Should().Be(expectedResult); [TestCase("/test", "/test/mydir/")]
[TestCase("/test/", "/test/mydir")]
public void posix_path_should_be_parent(string parentPath, string childPath)
{
parentPath.IsParentPath(childPath).Should().Be(true);
} }
[TestCase(@"C:\Test\mydir", @"C:\Test")] [TestCase(@"C:\Test\mydir", @"C:\Test")]
@ -145,39 +150,57 @@ namespace NzbDrone.Common.Test
[TestCase(@"C:\", null)] [TestCase(@"C:\", null)]
[TestCase(@"\\server\share", null)] [TestCase(@"\\server\share", null)]
[TestCase(@"\\server\share\test", @"\\server\share")] [TestCase(@"\\server\share\test", @"\\server\share")]
public void path_should_return_parent_windows(string path, string parentPath) public void windows_path_should_return_parent(string path, string parentPath)
{ {
WindowsOnly();
path.GetParentPath().Should().Be(parentPath); path.GetParentPath().Should().Be(parentPath);
} }
[TestCase(@"/", null)] [TestCase(@"/", null)]
[TestCase(@"/test", "/")] [TestCase(@"/test", "/")]
public void path_should_return_parent_mono(string path, string parentPath) [TestCase(@"/test/tv", "/test")]
public void unix_path_should_return_parent(string path, string parentPath)
{ {
PosixOnly();
path.GetParentPath().Should().Be(parentPath); path.GetParentPath().Should().Be(parentPath);
} }
[TestCase(@"C:\Test\mydir", "Test")] [TestCase(@"C:\Test\mydir", "Test")]
[TestCase(@"C:\Test\", @"C:\")] [TestCase(@"C:\Test\", @"C:\")]
[TestCase(@"C:\Test", @"C:\")]
[TestCase(@"C:\", null)] [TestCase(@"C:\", null)]
[TestCase(@"\\server\share", null)] [TestCase(@"\\server\share", null)]
[TestCase(@"\\server\share\test", @"\\server\share")] [TestCase(@"\\server\share\test", @"\\server\share")]
public void path_should_return_parent_name_windows(string path, string parentPath) public void path_should_return_parent_name_windows(string path, string parentPath)
{ {
WindowsOnly();
path.GetParentName().Should().Be(parentPath); path.GetParentName().Should().Be(parentPath);
} }
[TestCase(@"/", null)] [TestCase(@"/", null)]
[TestCase(@"/test", "/")] [TestCase(@"/test", "/")]
[TestCase(@"/test/tv", "test")]
public void path_should_return_parent_name_mono(string path, string parentPath) public void path_should_return_parent_name_mono(string path, string parentPath)
{ {
PosixOnly();
path.GetParentName().Should().Be(parentPath); path.GetParentName().Should().Be(parentPath);
} }
[TestCase(@"C:\Test\mydir", "mydir")]
[TestCase(@"C:\Test\", "Test")]
[TestCase(@"C:\Test", "Test")]
[TestCase(@"C:\", "C:\\")]
[TestCase(@"\\server\share", @"\\server\share")]
[TestCase(@"\\server\share\test", "test")]
public void path_should_return_directory_name_windows(string path, string parentPath)
{
path.GetDirectoryName().Should().Be(parentPath);
}
[TestCase(@"/", "/")]
[TestCase(@"/test", "test")]
[TestCase(@"/test/tv", "tv")]
public void path_should_return_directory_name_mono(string path, string parentPath)
{
path.GetDirectoryName().Should().Be(parentPath);
}
[Test] [Test]
public void path_should_return_parent_for_oversized_path() public void path_should_return_parent_for_oversized_path()
{ {

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Disk namespace NzbDrone.Common.Disk
@ -9,6 +10,8 @@ namespace NzbDrone.Common.Disk
private readonly string _path; private readonly string _path;
private readonly OsPathKind _kind; private readonly OsPathKind _kind;
private static readonly Regex UncPathRegex = new Regex(@"(?<unc>^\\\\(?:\?\\UNC\\)?[^\\]+\\[^\\]+)(?:\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public OsPath(string path) public OsPath(string path)
{ {
if (path == null) if (path == null)
@ -96,6 +99,19 @@ namespace NzbDrone.Common.Disk
return path; return path;
} }
private static string TrimTrailingSlash(string path, OsPathKind kind)
{
switch (kind)
{
case OsPathKind.Windows when !path.EndsWith(":\\"):
return path.TrimEnd('\\');
case OsPathKind.Unix when path != "/":
return path.TrimEnd('/');
}
return path;
}
public OsPathKind Kind => _kind; public OsPathKind Kind => _kind;
public bool IsWindowsPath => _kind == OsPathKind.Windows; public bool IsWindowsPath => _kind == OsPathKind.Windows;
@ -130,7 +146,19 @@ namespace NzbDrone.Common.Disk
if (index == -1) if (index == -1)
{ {
return new OsPath(null); return Null;
}
var rootLength = GetRootLength();
if (rootLength == _path.Length)
{
return Null;
}
if (rootLength > index + 1)
{
return new OsPath(_path.Substring(0, rootLength));
} }
return new OsPath(_path.Substring(0, index), _kind).AsDirectory(); return new OsPath(_path.Substring(0, index), _kind).AsDirectory();
@ -139,6 +167,8 @@ namespace NzbDrone.Common.Disk
public string FullPath => _path; public string FullPath => _path;
public string PathWithoutTrailingSlash => TrimTrailingSlash(_path, _kind);
public string FileName public string FileName
{ {
get get
@ -161,6 +191,30 @@ namespace NzbDrone.Common.Disk
} }
} }
public string Name
{
// Meant to behave similar to DirectoryInfo.Name
get
{
var index = GetFileNameIndex();
if (index == -1)
{
return PathWithoutTrailingSlash;
}
var rootLength = GetRootLength();
if (rootLength > index + 1)
{
return _path.Substring(0, rootLength);
}
return TrimTrailingSlash(_path.Substring(index).TrimStart('/', '\\'), _kind);
}
}
public bool IsValid => _path.IsPathValid(PathValidationType.CurrentOs); public bool IsValid => _path.IsPathValid(PathValidationType.CurrentOs);
private int GetFileNameIndex() private int GetFileNameIndex()
@ -190,11 +244,50 @@ namespace NzbDrone.Common.Disk
return index; return index;
} }
private int GetRootLength()
{
if (!IsRooted)
{
return 0;
}
if (_kind == OsPathKind.Unix)
{
return 1;
}
if (_kind == OsPathKind.Windows)
{
if (HasWindowsDriveLetter(_path))
{
return 3;
}
var uncMatch = UncPathRegex.Match(_path);
// \\?\UNC\server\share\ or \\server\share
if (uncMatch.Success)
{
return uncMatch.Groups["unc"].Length;
}
// \\?\C:\
if (_path.StartsWith(@"\\?\"))
{
return 7;
}
}
return 0;
}
private string[] GetFragments() private string[] GetFragments()
{ {
return _path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries); return _path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
} }
public static OsPath Null => new (null);
public override string ToString() public override string ToString()
{ {
return _path; return _path;
@ -267,6 +360,11 @@ namespace NzbDrone.Common.Disk
} }
public bool Equals(OsPath other) public bool Equals(OsPath other)
{
return Equals(other, false);
}
public bool Equals(OsPath other, bool ignoreTrailingSlash)
{ {
if (ReferenceEquals(other, null)) if (ReferenceEquals(other, null))
{ {
@ -278,8 +376,8 @@ namespace NzbDrone.Common.Disk
return true; return true;
} }
var left = _path; var left = ignoreTrailingSlash ? PathWithoutTrailingSlash : _path;
var right = other._path; var right = ignoreTrailingSlash ? other.PathWithoutTrailingSlash : other._path;
if (Kind == OsPathKind.Windows || other.Kind == OsPathKind.Windows) if (Kind == OsPathKind.Windows || other.Kind == OsPathKind.Windows)
{ {

@ -93,26 +93,23 @@ namespace NzbDrone.Common.Extensions
public static string GetParentPath(this string childPath) public static string GetParentPath(this string childPath)
{ {
var cleanPath = childPath.GetCleanPath(); var path = new OsPath(childPath).Directory;
if (cleanPath.IsNullOrWhiteSpace()) return path == OsPath.Null ? null : path.PathWithoutTrailingSlash;
{
return null;
}
return Directory.GetParent(cleanPath)?.FullName;
} }
public static string GetParentName(this string childPath) public static string GetParentName(this string childPath)
{ {
var cleanPath = childPath.GetCleanPath(); var path = new OsPath(childPath).Directory;
if (cleanPath.IsNullOrWhiteSpace()) return path == OsPath.Null ? null : path.Name;
{ }
return null;
} public static string GetDirectoryName(this string childPath)
{
var path = new OsPath(childPath);
return Directory.GetParent(cleanPath)?.Name; return path == OsPath.Null ? null : path.Name;
} }
public static string GetCleanPath(this string path) public static string GetCleanPath(this string path)
@ -126,27 +123,17 @@ namespace NzbDrone.Common.Extensions
public static bool IsParentPath(this string parentPath, string childPath) public static bool IsParentPath(this string parentPath, string childPath)
{ {
if (parentPath != "/" && !parentPath.EndsWith(":\\")) var parent = new OsPath(parentPath);
{ var child = new OsPath(childPath);
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar);
}
if (childPath != "/" && !parentPath.EndsWith(":\\"))
{
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar);
}
var parent = new DirectoryInfo(parentPath);
var child = new DirectoryInfo(childPath);
while (child.Parent != null) while (child.Directory != OsPath.Null)
{ {
if (child.Parent.FullName.Equals(parent.FullName, DiskProviderBase.PathStringComparison)) if (child.Directory.Equals(parent, true))
{ {
return true; return true;
} }
child = child.Parent; child = child.Directory;
} }
return false; return false;

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.RootFolders; using NzbDrone.Core.RootFolders;
@ -15,11 +16,13 @@ namespace NzbDrone.Core.Movies
{ {
private readonly IBuildFileNames _fileNameBuilder; private readonly IBuildFileNames _fileNameBuilder;
private readonly IRootFolderService _rootFolderService; private readonly IRootFolderService _rootFolderService;
private readonly Logger _logger;
public MoviePathBuilder(IBuildFileNames fileNameBuilder, IRootFolderService rootFolderService) public MoviePathBuilder(IBuildFileNames fileNameBuilder, IRootFolderService rootFolderService, Logger logger)
{ {
_fileNameBuilder = fileNameBuilder; _fileNameBuilder = fileNameBuilder;
_rootFolderService = rootFolderService; _rootFolderService = rootFolderService;
_logger = logger;
} }
public string BuildPath(Movie movie, bool useExistingRelativeFolder) public string BuildPath(Movie movie, bool useExistingRelativeFolder)
@ -42,7 +45,16 @@ namespace NzbDrone.Core.Movies
{ {
var rootFolderPath = _rootFolderService.GetBestRootFolderPath(movie.Path); var rootFolderPath = _rootFolderService.GetBestRootFolderPath(movie.Path);
return rootFolderPath.GetRelativePath(movie.Path); if (rootFolderPath.IsParentPath(movie.Path))
{
return rootFolderPath.GetRelativePath(movie.Path);
}
var directoryName = movie.Path.GetDirectoryName();
_logger.Warn("Unable to get relative path for movie path {0}, using movie folder name {1}", movie.Path, directoryName);
return directoryName;
} }
} }
} }

Loading…
Cancel
Save