New: Author folder hint when selecting a root folder while adding a new author

(cherry picked from commit dd09f31abb4dd3f699bcff0a47577075300c70ee)

Fix AuthorFolderAsRootFolderValidator

(cherry picked from commit 0ce81e1ab69d43fde382cc4ae22cd46fe626dea7)
This commit is contained in:
Mark McDowall
2019-08-03 18:55:31 -07:00
committed by Bogdan
parent 0972d41bf8
commit f819e582cf
26 changed files with 225 additions and 29 deletions

View File

@@ -108,6 +108,15 @@ namespace NzbDrone.Common.Extensions
return Directory.GetParent(cleanPath)?.FullName;
}
public static string GetCleanPath(this string path)
{
var cleanPath = OsInfo.IsWindows
? PARENT_PATH_END_SLASH_REGEX.Replace(path, "")
: path.TrimEnd(Path.DirectorySeparatorChar);
return cleanPath;
}
public static bool IsParentPath(this string parentPath, string childPath)
{
if (parentPath != "/" && !parentPath.EndsWith(":\\"))

View File

@@ -11,7 +11,11 @@
"AddListExclusion": "Add List Exclusion",
"AddMissing": "Add missing",
"AddNew": "Add New",
"AddNewBook": "Add New Book",
"AddNewAuthor": "Add New Author",
"AddNewAuthorRootFolderHelpText": "'{folder}' subfolder will be created automatically",
"AddNewItem": "Add New Item",
"AddRootFolder": "Add Root Folder",
"AddedAuthorSettings": "Added Author Settings",
"AddingTag": "Adding tag",
"AgeWhenGrabbed": "Age (when grabbed)",

View File

@@ -59,7 +59,8 @@ namespace Readarr.Api.V1.Author
AuthorAncestorValidator authorAncestorValidator,
SystemFolderValidator systemFolderValidator,
QualityProfileExistsValidator qualityProfileExistsValidator,
MetadataProfileExistsValidator metadataProfileExistsValidator)
MetadataProfileExistsValidator metadataProfileExistsValidator,
AuthorFolderAsRootFolderValidator authorFolderAsRootFolderValidator)
: base(signalRBroadcaster)
{
_authorService = authorService;
@@ -89,7 +90,10 @@ namespace Readarr.Api.V1.Author
SharedValidator.RuleFor(s => s.MetadataProfileId).SetValidator(metadataProfileExistsValidator);
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath)
.IsValidPath()
.SetValidator(authorFolderAsRootFolderValidator)
.When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.AuthorName).NotEmpty();
PostValidator.RuleFor(s => s.ForeignAuthorId).NotEmpty().SetValidator(authorExistsValidator);

View File

@@ -0,0 +1,49 @@
using System;
using System.IO;
using FluentValidation.Validators;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Organizer;
namespace Readarr.Api.V1.Author
{
public class AuthorFolderAsRootFolderValidator : PropertyValidator
{
private readonly IBuildFileNames _fileNameBuilder;
public AuthorFolderAsRootFolderValidator(IBuildFileNames fileNameBuilder)
{
_fileNameBuilder = fileNameBuilder;
}
protected override string GetDefaultMessageTemplate() => "Root folder path contains author folder";
protected override bool IsValid(PropertyValidatorContext context)
{
if (context.PropertyValue == null)
{
return true;
}
var authorResource = context.ParentContext.InstanceToValidate as AuthorResource;
if (authorResource == null)
{
return true;
}
var rootFolderPath = context.PropertyValue.ToString();
var rootFolder = new DirectoryInfo(rootFolderPath).Name;
var author = authorResource.ToModel();
var authorFolder = _fileNameBuilder.GetAuthorFolder(author);
if (authorFolder == rootFolder)
{
return false;
}
var distance = authorFolder.LevenshteinDistance(rootFolder);
return distance >= Math.Max(1, authorFolder.Length * 0.2);
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Linq;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Organizer;
using Readarr.Http;
namespace Readarr.Api.V1.Author
@@ -11,11 +12,13 @@ namespace Readarr.Api.V1.Author
public class AuthorLookupController : Controller
{
private readonly ISearchForNewAuthor _searchProxy;
private readonly IBuildFileNames _fileNameBuilder;
private readonly IMapCoversToLocal _coverMapper;
public AuthorLookupController(ISearchForNewAuthor searchProxy, IMapCoversToLocal coverMapper)
public AuthorLookupController(ISearchForNewAuthor searchProxy, IBuildFileNames fileNameBuilder, IMapCoversToLocal coverMapper)
{
_searchProxy = searchProxy;
_fileNameBuilder = fileNameBuilder;
_coverMapper = coverMapper;
}
@@ -41,6 +44,8 @@ namespace Readarr.Api.V1.Author
resource.RemotePoster = poster.Url;
}
resource.Folder = _fileNameBuilder.GetAuthorFolder(currentAuthor);
yield return resource;
}
}

View File

@@ -45,6 +45,7 @@ namespace Readarr.Api.V1.Author
public NewItemMonitorTypes MonitorNewItems { get; set; }
public string RootFolderPath { get; set; }
public string Folder { get; set; }
public List<string> Genres { get; set; }
public string CleanName { get; set; }
public string SortName { get; set; }

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Books;
using NzbDrone.Core.Books.Calibre;
using NzbDrone.Core.RootFolders;
@@ -47,7 +48,7 @@ namespace Readarr.Api.V1.RootFolders
Id = model.Id,
Name = model.Name,
Path = model.Path,
Path = model.Path.GetCleanPath(),
DefaultMetadataProfileId = model.DefaultMetadataProfileId,
DefaultQualityProfileId = model.DefaultQualityProfileId,
DefaultMonitorOption = model.DefaultMonitorOption,

View File

@@ -4,6 +4,7 @@ using System.Linq;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Organizer;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http;
@@ -14,11 +15,13 @@ namespace Readarr.Api.V1.Search
public class SearchController : Controller
{
private readonly ISearchForNewEntity _searchProxy;
private readonly IBuildFileNames _fileNameBuilder;
private readonly IMapCoversToLocal _coverMapper;
public SearchController(ISearchForNewEntity searchProxy, IMapCoversToLocal coverMapper)
public SearchController(ISearchForNewEntity searchProxy, IBuildFileNames fileNameBuilder, IMapCoversToLocal coverMapper)
{
_searchProxy = searchProxy;
_fileNameBuilder = fileNameBuilder;
_coverMapper = coverMapper;
}
@@ -50,6 +53,8 @@ namespace Readarr.Api.V1.Search
{
resource.Author.RemotePoster = poster.Url;
}
resource.Author.Folder = _fileNameBuilder.GetAuthorFolder(author);
}
else if (result is NzbDrone.Core.Books.Book book)
{
@@ -67,6 +72,8 @@ namespace Readarr.Api.V1.Search
{
resource.Book.RemoteCover = cover.Url;
}
resource.Book.Author.Folder = _fileNameBuilder.GetAuthorFolder(book.Author);
}
else
{