mirror of
https://github.com/Readarr/Readarr
synced 2026-04-23 21:31:09 +02:00
Fixed: Sort authors by lastname, firstname
This commit is contained in:
parent
62221c2a7f
commit
96db74494a
26 changed files with 279 additions and 27 deletions
|
|
@ -78,7 +78,7 @@ class BlacklistRow extends Component {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (name === 'authors.sortName') {
|
||||
if (name === 'authorMetadata.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<AuthorNameLink
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ class HistoryRow extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
if (name === 'authors.sortName') {
|
||||
if (name === 'authorMetadata.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<AuthorNameLink
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ class QueueRow extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
if (name === 'authors.sortName') {
|
||||
if (name === 'authorMetadata.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export const defaultState = {
|
|||
|
||||
columns: [
|
||||
{
|
||||
name: 'authors.sortName',
|
||||
name: 'authorMetadata.sortName',
|
||||
label: 'Author Name',
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export const defaultState = {
|
|||
isModifiable: false
|
||||
},
|
||||
{
|
||||
name: 'authors.sortName',
|
||||
name: 'authorMetadata.sortName',
|
||||
label: 'Author',
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export const defaultState = {
|
|||
isModifiable: false
|
||||
},
|
||||
{
|
||||
name: 'authors.sortName',
|
||||
name: 'authorMetadata.sortName',
|
||||
label: 'Author',
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export const defaultState = {
|
|||
|
||||
columns: [
|
||||
{
|
||||
name: 'authors.sortName',
|
||||
name: 'authorMetadata.sortName',
|
||||
label: 'Author',
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
|
|
@ -91,7 +91,7 @@ export const defaultState = {
|
|||
|
||||
columns: [
|
||||
{
|
||||
name: 'authors.sortName',
|
||||
name: 'authorMetadata.sortName',
|
||||
label: 'Author',
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import migrateAddAuthorDefaults from './migrateAddAuthorDefaults';
|
||||
import migrateAuthorSortKey from './migrateAuthorSortKey';
|
||||
|
||||
export default function migrate(persistedState) {
|
||||
migrateAddAuthorDefaults(persistedState);
|
||||
migrateAuthorSortKey(persistedState);
|
||||
}
|
||||
|
|
|
|||
15
frontend/src/Store/Migrators/migrateAuthorSortKey.js
Normal file
15
frontend/src/Store/Migrators/migrateAuthorSortKey.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { get, set } from 'lodash';
|
||||
|
||||
const TABLES_TO_MIGRATE = ['blacklist', 'history', 'queue.paged', 'wanted.missing', 'wanted.cutoffUnmet'];
|
||||
|
||||
export default function migrateAuthorSortKey(persistedState) {
|
||||
|
||||
for (const table of TABLES_TO_MIGRATE) {
|
||||
const key = `${table}.sortKey`;
|
||||
const sortKey = get(persistedState, key);
|
||||
|
||||
if (sortKey === 'authors.sortName') {
|
||||
set(persistedState, key, 'authorMetadata.sortName');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ function CutoffUnmetRow(props) {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (name === 'authors.sortName') {
|
||||
if (name === 'authorMetadata.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<AuthorNameLink
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ function MissingRow(props) {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (name === 'authors.sortName') {
|
||||
if (name === 'authorMetadata.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<AuthorNameLink
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Test.ExtensionTests.StringExtensionTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ToSortNameFixture
|
||||
{
|
||||
[TestCase("a[b]c(d)e{f}g<h>i", "aceg<h>i")]
|
||||
[TestCase("a[[b]c(d)e{f}]g(h(i)j[k]l{m})n{{{o}}}p", "agnp")]
|
||||
[TestCase("a[b(c]d)e", "ae")]
|
||||
[TestCase("a{b(c}d)e", "ae")]
|
||||
[TestCase("a]b}c)d", "abcd")]
|
||||
[TestCase("a[b]c]d(e)f{g)h}i}j)k]l", "acdfijkl")]
|
||||
[TestCase("a]b[c", "ab")]
|
||||
[TestCase("a(b[c]d{e}f", "a")]
|
||||
[TestCase("a{b}c{d[e]f(g)h", "ac")]
|
||||
public void should_remove_brackets(string input, string expected)
|
||||
{
|
||||
input.RemoveBracketedText().Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("Aristotle", "Aristotle")]
|
||||
[TestCase("Mr. Dr Prof.", "Mr. Dr Prof.")]
|
||||
[TestCase("Senior Inc", "Senior Inc")]
|
||||
[TestCase("Don \"Team\" Smith", "Smith, Don \"Team\"")]
|
||||
[TestCase("Don Team Smith", "Don Team Smith")]
|
||||
[TestCase("National Lampoon", "National Lampoon")]
|
||||
[TestCase("Jane Doe", "Doe, Jane")]
|
||||
[TestCase("Mrs. Jane Q. Doe III", "Doe, Jane Q. III")]
|
||||
[TestCase("Leonardo Da Vinci", "Da Vinci, Leonardo")]
|
||||
[TestCase("Van Gogh", "Van Gogh")]
|
||||
[TestCase("Van", "Van")]
|
||||
[TestCase("John [x]von Neumann (III)", "von Neumann, John")]
|
||||
public void should_get_sort_name(string input, string expected)
|
||||
{
|
||||
input.ToSortName().Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -210,5 +210,150 @@ public static double LevenshteinCoefficient(this string a, string b)
|
|||
{
|
||||
return 1.0 - ((double)a.LevenshteinDistance(b) / Math.Max(a.Length, b.Length));
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> Copywords = new HashSet<string>
|
||||
{
|
||||
"agency", "corporation", "company", "co.", "council",
|
||||
"committee", "inc.", "institute", "national",
|
||||
"society", "club", "team"
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> SurnamePrefixes = new HashSet<string>
|
||||
{
|
||||
"da", "de", "di", "la", "le", "van", "von"
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> Prefixes = new HashSet<string>
|
||||
{
|
||||
"mr", "mr.", "mrs", "mrs.", "ms", "ms.", "dr", "dr.", "prof", "prof."
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> Suffixes = new HashSet<string>
|
||||
{
|
||||
"jr", "sr", "inc", "ph.d", "phd",
|
||||
"md", "m.d", "i", "ii", "iii", "iv",
|
||||
"junior", "senior"
|
||||
};
|
||||
|
||||
private static readonly Dictionary<char, char> Brackets = new Dictionary<char, char>
|
||||
{
|
||||
{ '(', ')' },
|
||||
{ '[', ']' },
|
||||
{ '{', '}' }
|
||||
};
|
||||
|
||||
private static readonly Dictionary<char, char> RMap = Brackets.ToDictionary(x => x.Value, x => x.Key);
|
||||
|
||||
public static string RemoveBracketedText(this string input)
|
||||
{
|
||||
var counts = Brackets.ToDictionary(x => x.Key, y => 0);
|
||||
var total = 0;
|
||||
var buf = new List<char>(input.Length);
|
||||
|
||||
foreach (var c in input)
|
||||
{
|
||||
if (Brackets.ContainsKey(c))
|
||||
{
|
||||
counts[c] += 1;
|
||||
total += 1;
|
||||
}
|
||||
else if (RMap.ContainsKey(c))
|
||||
{
|
||||
var idx = RMap[c];
|
||||
if (counts[idx] > 0)
|
||||
{
|
||||
counts[idx] -= 1;
|
||||
total -= 1;
|
||||
}
|
||||
}
|
||||
else if (total < 1)
|
||||
{
|
||||
buf.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
return new string(buf.ToArray());
|
||||
}
|
||||
|
||||
public static string ToSortName(this string author)
|
||||
{
|
||||
// ported from https://github.com/kovidgoyal/calibre/blob/master/src/calibre/ebooks/metadata/__init__.py
|
||||
if (author == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var sauthor = author.RemoveBracketedText().Trim();
|
||||
|
||||
var tokens = sauthor.Split();
|
||||
|
||||
if (tokens.Length < 2)
|
||||
{
|
||||
return author;
|
||||
}
|
||||
|
||||
var ltoks = tokens.Select(x => x.ToLowerInvariant()).ToHashSet();
|
||||
|
||||
if (ltoks.Intersect(Copywords).Any())
|
||||
{
|
||||
return author;
|
||||
}
|
||||
|
||||
if (tokens.Length == 2 && SurnamePrefixes.Contains(tokens[0].ToLowerInvariant()))
|
||||
{
|
||||
return author;
|
||||
}
|
||||
|
||||
int first;
|
||||
for (first = 0; first < tokens.Length; first++)
|
||||
{
|
||||
if (!Prefixes.Contains(tokens[first].ToLowerInvariant()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (first == tokens.Length)
|
||||
{
|
||||
return author;
|
||||
}
|
||||
|
||||
int last;
|
||||
for (last = tokens.Length - 1; last >= first; last--)
|
||||
{
|
||||
if (!Suffixes.Contains(tokens[last].ToLowerInvariant()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (last < first)
|
||||
{
|
||||
return author;
|
||||
}
|
||||
|
||||
var suffix = tokens.TakeLast(tokens.Length - last - 1).ConcatToString(" ");
|
||||
|
||||
if (last > first && SurnamePrefixes.Contains(tokens[last - 1].ToLowerInvariant()))
|
||||
{
|
||||
tokens[last - 1] += ' ' + tokens[last];
|
||||
last -= 1;
|
||||
}
|
||||
|
||||
var atokens = new[] { tokens[last] }.Concat(tokens.Skip(first).Take(last - first)).ToList();
|
||||
var addComma = atokens.Count > 1;
|
||||
|
||||
if (suffix.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
atokens.Add(suffix);
|
||||
}
|
||||
|
||||
if (addComma)
|
||||
{
|
||||
atokens[0] += ',';
|
||||
}
|
||||
|
||||
return atokens.ConcatToString(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.MetadataSource.Goodreads;
|
||||
|
|
@ -83,7 +84,6 @@ private void ValidateAuthor(Author author)
|
|||
author.Should().NotBeNull();
|
||||
author.Name.Should().NotBeNullOrWhiteSpace();
|
||||
author.CleanName.Should().Be(Parser.Parser.CleanAuthorName(author.Name));
|
||||
author.SortName.Should().Be(Parser.Parser.NormalizeTitle(author.Name));
|
||||
author.Metadata.Value.TitleSlug.Should().NotBeNullOrWhiteSpace();
|
||||
author.Metadata.Value.Overview.Should().NotBeNullOrWhiteSpace();
|
||||
author.Metadata.Value.Images.Should().NotBeEmpty();
|
||||
|
|
|
|||
|
|
@ -34,9 +34,13 @@ public List<Blacklist> BlacklistedByAuthor(int authorId)
|
|||
return Query(b => b.AuthorId == authorId);
|
||||
}
|
||||
|
||||
protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join<Blacklist, Author>((b, m) => b.AuthorId == m.Id);
|
||||
protected override IEnumerable<Blacklist> PagedQuery(SqlBuilder builder) => _database.QueryJoined<Blacklist, Author>(builder, (bl, author) =>
|
||||
protected override SqlBuilder PagedBuilder() => new SqlBuilder()
|
||||
.Join<Blacklist, Author>((b, m) => b.AuthorId == m.Id)
|
||||
.Join<Author, AuthorMetadata>((l, r) => l.AuthorMetadataId == r.Id);
|
||||
protected override IEnumerable<Blacklist> PagedQuery(SqlBuilder builder) => _database.QueryJoined<Blacklist, Author, AuthorMetadata>(builder,
|
||||
(bl, author, metadata) =>
|
||||
{
|
||||
author.Metadata = metadata;
|
||||
bl.Author = author;
|
||||
return bl;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ public Author()
|
|||
// These correspond to columns in the Authors table
|
||||
public int AuthorMetadataId { get; set; }
|
||||
public string CleanName { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public DateTime? LastInfoSync { get; set; }
|
||||
public string Path { get; set; }
|
||||
|
|
@ -64,7 +63,6 @@ public override string ToString()
|
|||
public override void UseMetadataFrom(Author other)
|
||||
{
|
||||
CleanName = other.CleanName;
|
||||
SortName = other.SortName;
|
||||
}
|
||||
|
||||
public override void UseDbFieldsFrom(Author other)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public AuthorMetadata()
|
|||
public string ForeignAuthorId { get; set; }
|
||||
public string TitleSlug { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public List<string> Aliases { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public string Disambiguation { get; set; }
|
||||
|
|
@ -42,6 +43,7 @@ public override void UseMetadataFrom(AuthorMetadata other)
|
|||
ForeignAuthorId = other.ForeignAuthorId;
|
||||
TitleSlug = other.TitleSlug;
|
||||
Name = other.Name;
|
||||
SortName = other.SortName;
|
||||
Aliases = other.Aliases;
|
||||
Overview = other.Overview.IsNullOrWhiteSpace() ? Overview : other.Overview;
|
||||
Disambiguation = other.Disambiguation;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ public Book FindBySlug(string titleSlug)
|
|||
#pragma warning disable CS0472
|
||||
private SqlBuilder BooksWithoutFilesBuilder(DateTime currentTime) => Builder()
|
||||
.Join<Book, Author>((l, r) => l.AuthorMetadataId == r.AuthorMetadataId)
|
||||
.Join<Author, AuthorMetadata>((l, r) => l.AuthorMetadataId == r.Id)
|
||||
.Join<Book, Edition>((b, e) => b.Id == e.BookId)
|
||||
.LeftJoin<Edition, BookFile>((t, f) => t.Id == f.EditionId)
|
||||
.Where<BookFile>(f => f.Id == null)
|
||||
|
|
@ -110,6 +111,7 @@ public PagingSpec<Book> BooksWithoutFiles(PagingSpec<Book> pagingSpec)
|
|||
|
||||
private SqlBuilder BooksWhereCutoffUnmetBuilder(List<QualitiesBelowCutoff> qualitiesBelowCutoff) => Builder()
|
||||
.Join<Book, Author>((l, r) => l.AuthorMetadataId == r.AuthorMetadataId)
|
||||
.Join<Author, AuthorMetadata>((l, r) => l.AuthorMetadataId == r.Id)
|
||||
.Join<Book, Edition>((b, e) => b.Id == e.BookId)
|
||||
.LeftJoin<Edition, BookFile>((t, f) => t.Id == f.EditionId)
|
||||
.Where<Edition>(e => e.Monitored == true)
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@ private Author SetPropertiesAndValidate(Author newAuthor)
|
|||
|
||||
newAuthor.Path = path;
|
||||
newAuthor.CleanName = newAuthor.Metadata.Value.Name.CleanAuthorName();
|
||||
newAuthor.SortName = Parser.Parser.NormalizeTitle(newAuthor.Metadata.Value.Name).ToLower();
|
||||
newAuthor.Added = DateTime.UtcNow;
|
||||
|
||||
if (newAuthor.AddOptions != null && newAuthor.AddOptions.Monitor == MonitorTypes.None)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
using System.Data;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(009)]
|
||||
public class update_author_sort_name : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("AuthorMetadata").AddColumn("SortName").AsString().Nullable();
|
||||
Execute.WithConnection(MigrateAuthorSortName);
|
||||
Alter.Table("AuthorMetadata").AlterColumn("SortName").AsString().NotNullable();
|
||||
|
||||
Delete.Column("SortName").FromTable("Authors");
|
||||
}
|
||||
|
||||
private void MigrateAuthorSortName(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var rows = conn.Query<AuthorName>("SELECT AuthorMetadata.Id, AuthorMetadata.Name FROM AuthorMetadata", transaction: tran);
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
row.SortName = row.Name.ToSortName().ToLower();
|
||||
}
|
||||
|
||||
var sql = "UPDATE AuthorMetadata SET SortName = @SortName WHERE Id = @Id";
|
||||
conn.Execute(sql, rows, transaction: tran);
|
||||
}
|
||||
|
||||
private class AuthorName : ModelBase
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string SortName { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,11 +104,13 @@ public void DeleteForAuthor(int authorId)
|
|||
|
||||
protected override SqlBuilder PagedBuilder() => new SqlBuilder()
|
||||
.Join<History, Author>((h, a) => h.AuthorId == a.Id)
|
||||
.Join<Author, AuthorMetadata>((l, r) => l.AuthorMetadataId == r.Id)
|
||||
.Join<History, Book>((h, a) => h.BookId == a.Id);
|
||||
|
||||
protected override IEnumerable<History> PagedQuery(SqlBuilder builder) =>
|
||||
_database.QueryJoined<History, Author, Book>(builder, (history, author, book) =>
|
||||
_database.QueryJoined<History, Author, AuthorMetadata, Book>(builder, (history, author, metadata, book) =>
|
||||
{
|
||||
author.Metadata = metadata;
|
||||
history.Author = author;
|
||||
history.Book = book;
|
||||
return history;
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ public Author GetAuthorInfo(string foreignAuthorId, bool useCache = true)
|
|||
Metadata = MapAuthor(resource)
|
||||
};
|
||||
author.CleanName = Parser.Parser.CleanAuthorName(author.Metadata.Value.Name);
|
||||
author.SortName = Parser.Parser.NormalizeTitle(author.Metadata.Value.Name);
|
||||
|
||||
// we can only get a rating from the author list page...
|
||||
var listResource = GetAuthorBooksPageResource(foreignAuthorId, 10, 1);
|
||||
|
|
@ -532,6 +531,8 @@ private static AuthorMetadata MapAuthor(AuthorResource resource)
|
|||
Status = resource.DiedOnDate < DateTime.UtcNow ? AuthorStatusType.Ended : AuthorStatusType.Continuing
|
||||
};
|
||||
|
||||
author.SortName = author.Name.ToSortName().ToLower();
|
||||
|
||||
if (!NoPhotoRegex.IsMatch(resource.LargeImageUrl))
|
||||
{
|
||||
author.Images.Add(new MediaCover.MediaCover
|
||||
|
|
@ -555,6 +556,8 @@ private static AuthorMetadata MapAuthor(AuthorSummaryResource resource)
|
|||
TitleSlug = resource.Id.ToString()
|
||||
};
|
||||
|
||||
author.SortName = author.Name.ToSortName().ToLower();
|
||||
|
||||
if (resource.RatingsCount.HasValue)
|
||||
{
|
||||
author.Ratings = new Ratings
|
||||
|
|
@ -704,6 +707,7 @@ private Book MapSearchResult(WorkResource resource)
|
|||
{
|
||||
ForeignAuthorId = resource.BestBook.AuthorId.ToString(),
|
||||
Name = resource.BestBook.AuthorName,
|
||||
SortName = resource.BestBook.AuthorName.ToSortName().ToLower(),
|
||||
TitleSlug = resource.BestBook.AuthorId.ToString()
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -276,7 +276,6 @@ private Author MapAuthor(AuthorResource resource)
|
|||
{
|
||||
Metadata = metadata,
|
||||
CleanName = Parser.Parser.CleanAuthorName(metadata.Name),
|
||||
SortName = Parser.Parser.NormalizeTitle(metadata.Name),
|
||||
Books = books,
|
||||
Series = series
|
||||
};
|
||||
|
|
@ -316,6 +315,8 @@ private static AuthorMetadata MapAuthor(AuthorSummaryResource resource)
|
|||
Ratings = new Ratings { Votes = resource.RatingsCount, Value = (decimal)resource.AverageRating }
|
||||
};
|
||||
|
||||
author.SortName = author.Name.ToSortName().ToLower();
|
||||
|
||||
if (resource.ImageUrl.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
author.Images.Add(new MediaCover.MediaCover
|
||||
|
|
|
|||
|
|
@ -576,11 +576,6 @@ public static ParsedBookInfo ParseBookTitle(string title)
|
|||
return null;
|
||||
}
|
||||
|
||||
public static string ToSortName(this string name)
|
||||
{
|
||||
return name.Split(' ', 2).Reverse().ConcatToString(", ");
|
||||
}
|
||||
|
||||
public static string CleanAuthorName(this string name)
|
||||
{
|
||||
// If Title only contains numbers return it as is.
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public static AuthorResource ToResource(this NzbDrone.Core.Books.Author model)
|
|||
AuthorName = model.Name,
|
||||
|
||||
//AlternateTitles
|
||||
SortName = model.SortName,
|
||||
SortName = model.Metadata.Value.SortName,
|
||||
|
||||
Status = model.Metadata.Value.Status,
|
||||
Overview = model.Metadata.Value.Overview,
|
||||
|
|
@ -119,6 +119,7 @@ public static NzbDrone.Core.Books.Author ToModel(this AuthorResource resource)
|
|||
ForeignAuthorId = resource.ForeignAuthorId,
|
||||
TitleSlug = resource.TitleSlug,
|
||||
Name = resource.AuthorName,
|
||||
SortName = resource.SortName,
|
||||
Status = resource.Status,
|
||||
Overview = resource.Overview,
|
||||
Links = resource.Links,
|
||||
|
|
@ -128,7 +129,6 @@ public static NzbDrone.Core.Books.Author ToModel(this AuthorResource resource)
|
|||
},
|
||||
|
||||
//AlternateTitles
|
||||
SortName = resource.SortName,
|
||||
Path = resource.Path,
|
||||
QualityProfileId = resource.QualityProfileId,
|
||||
MetadataProfileId = resource.MetadataProfileId,
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ public PagingResource<QueueResource> GetQueue(bool includeUnknownAuthorItems = f
|
|||
case "status":
|
||||
return q => q.Status;
|
||||
case "authors.sortName":
|
||||
return q => q.Author?.SortName ?? string.Empty;
|
||||
return q => q.Author?.Metadata.Value.SortName ?? string.Empty;
|
||||
case "title":
|
||||
return q => q.Title;
|
||||
case "book":
|
||||
|
|
|
|||
Loading…
Reference in a new issue