From 96db74494a715972219994b5a18ec9d4de15e8b4 Mon Sep 17 00:00:00 2001 From: ta264 Date: Thu, 13 May 2021 21:31:46 +0100 Subject: [PATCH] Fixed: Sort authors by lastname, firstname --- .../src/Activity/Blacklist/BlacklistRow.js | 2 +- frontend/src/Activity/History/HistoryRow.js | 2 +- frontend/src/Activity/Queue/QueueRow.js | 2 +- .../src/Store/Actions/blacklistActions.js | 2 +- frontend/src/Store/Actions/historyActions.js | 2 +- frontend/src/Store/Actions/queueActions.js | 2 +- frontend/src/Store/Actions/wantedActions.js | 4 +- frontend/src/Store/Migrators/migrate.js | 2 + .../Store/Migrators/migrateAuthorSortKey.js | 15 ++ .../src/Wanted/CutoffUnmet/CutoffUnmetRow.js | 2 +- frontend/src/Wanted/Missing/MissingRow.js | 2 +- .../StringExtensionTests/ToSortNameFixture.cs | 42 +++++ .../Extensions/StringExtensions.cs | 145 ++++++++++++++++++ .../Goodreads/GoodreadsProxyFixture.cs | 2 +- .../Blacklisting/BlacklistRepository.cs | 8 +- src/NzbDrone.Core/Books/Model/Author.cs | 2 - .../Books/Model/AuthorMetadata.cs | 2 + .../Books/Repositories/BookRepository.cs | 2 + .../Books/Services/AddAuthorService.cs | 1 - .../Migration/009_update_author_sort_name.cs | 41 +++++ .../History/HistoryRepository.cs | 4 +- .../Goodreads/GoodreadsProxy.cs | 6 +- .../MetadataSource/SkyHook/SkyHookProxy.cs | 3 +- src/NzbDrone.Core/Parser/Parser.cs | 5 - src/Readarr.Api.V1/Author/AuthorResource.cs | 4 +- src/Readarr.Api.V1/Queue/QueueController.cs | 2 +- 26 files changed, 279 insertions(+), 27 deletions(-) create mode 100644 frontend/src/Store/Migrators/migrateAuthorSortKey.js create mode 100644 src/NzbDrone.Common.Test/ExtensionTests/StringExtensionTests/ToSortNameFixture.cs create mode 100644 src/NzbDrone.Core/Datastore/Migration/009_update_author_sort_name.cs diff --git a/frontend/src/Activity/Blacklist/BlacklistRow.js b/frontend/src/Activity/Blacklist/BlacklistRow.js index 2ee3191c0..1202bbd1b 100644 --- a/frontend/src/Activity/Blacklist/BlacklistRow.js +++ b/frontend/src/Activity/Blacklist/BlacklistRow.js @@ -78,7 +78,7 @@ class BlacklistRow extends Component { return null; } - if (name === 'authors.sortName') { + if (name === 'authorMetadata.sortName') { return ( { diff --git a/frontend/src/Store/Actions/blacklistActions.js b/frontend/src/Store/Actions/blacklistActions.js index 80865eab3..2d96a5efb 100644 --- a/frontend/src/Store/Actions/blacklistActions.js +++ b/frontend/src/Store/Actions/blacklistActions.js @@ -31,7 +31,7 @@ export const defaultState = { columns: [ { - name: 'authors.sortName', + name: 'authorMetadata.sortName', label: 'Author Name', isSortable: true, isVisible: true diff --git a/frontend/src/Store/Actions/historyActions.js b/frontend/src/Store/Actions/historyActions.js index 2a32902dd..64ae28bed 100644 --- a/frontend/src/Store/Actions/historyActions.js +++ b/frontend/src/Store/Actions/historyActions.js @@ -34,7 +34,7 @@ export const defaultState = { isModifiable: false }, { - name: 'authors.sortName', + name: 'authorMetadata.sortName', label: 'Author', isSortable: true, isVisible: true diff --git a/frontend/src/Store/Actions/queueActions.js b/frontend/src/Store/Actions/queueActions.js index fa93d8187..40007f082 100644 --- a/frontend/src/Store/Actions/queueActions.js +++ b/frontend/src/Store/Actions/queueActions.js @@ -63,7 +63,7 @@ export const defaultState = { isModifiable: false }, { - name: 'authors.sortName', + name: 'authorMetadata.sortName', label: 'Author', isSortable: true, isVisible: true diff --git a/frontend/src/Store/Actions/wantedActions.js b/frontend/src/Store/Actions/wantedActions.js index c94b44b41..fa14647a6 100644 --- a/frontend/src/Store/Actions/wantedActions.js +++ b/frontend/src/Store/Actions/wantedActions.js @@ -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 diff --git a/frontend/src/Store/Migrators/migrate.js b/frontend/src/Store/Migrators/migrate.js index df66eecd7..a5d0ddf8e 100644 --- a/frontend/src/Store/Migrators/migrate.js +++ b/frontend/src/Store/Migrators/migrate.js @@ -1,5 +1,7 @@ import migrateAddAuthorDefaults from './migrateAddAuthorDefaults'; +import migrateAuthorSortKey from './migrateAuthorSortKey'; export default function migrate(persistedState) { migrateAddAuthorDefaults(persistedState); + migrateAuthorSortKey(persistedState); } diff --git a/frontend/src/Store/Migrators/migrateAuthorSortKey.js b/frontend/src/Store/Migrators/migrateAuthorSortKey.js new file mode 100644 index 000000000..716f4f933 --- /dev/null +++ b/frontend/src/Store/Migrators/migrateAuthorSortKey.js @@ -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'); + } + } +} diff --git a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js index 686d0b6eb..3aa5b521e 100644 --- a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js +++ b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js @@ -48,7 +48,7 @@ function CutoffUnmetRow(props) { return null; } - if (name === 'authors.sortName') { + if (name === 'authorMetadata.sortName') { return ( i", "acegi")] + [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); + } + } +} diff --git a/src/NzbDrone.Common/Extensions/StringExtensions.cs b/src/NzbDrone.Common/Extensions/StringExtensions.cs index 4a07aa7b8..77f53b4e1 100644 --- a/src/NzbDrone.Common/Extensions/StringExtensions.cs +++ b/src/NzbDrone.Common/Extensions/StringExtensions.cs @@ -210,5 +210,150 @@ namespace NzbDrone.Common.Extensions { return 1.0 - ((double)a.LevenshteinDistance(b) / Math.Max(a.Length, b.Length)); } + + private static readonly HashSet Copywords = new HashSet + { + "agency", "corporation", "company", "co.", "council", + "committee", "inc.", "institute", "national", + "society", "club", "team" + }; + + private static readonly HashSet SurnamePrefixes = new HashSet + { + "da", "de", "di", "la", "le", "van", "von" + }; + + private static readonly HashSet Prefixes = new HashSet + { + "mr", "mr.", "mrs", "mrs.", "ms", "ms.", "dr", "dr.", "prof", "prof." + }; + + private static readonly HashSet Suffixes = new HashSet + { + "jr", "sr", "inc", "ph.d", "phd", + "md", "m.d", "i", "ii", "iii", "iv", + "junior", "senior" + }; + + private static readonly Dictionary Brackets = new Dictionary + { + { '(', ')' }, + { '[', ']' }, + { '{', '}' } + }; + + private static readonly Dictionary 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(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(" "); + } } } diff --git a/src/NzbDrone.Core.Test/MetadataSource/Goodreads/GoodreadsProxyFixture.cs b/src/NzbDrone.Core.Test/MetadataSource/Goodreads/GoodreadsProxyFixture.cs index bd3fe94c0..91d3b85a1 100644 --- a/src/NzbDrone.Core.Test/MetadataSource/Goodreads/GoodreadsProxyFixture.cs +++ b/src/NzbDrone.Core.Test/MetadataSource/Goodreads/GoodreadsProxyFixture.cs @@ -4,6 +4,7 @@ using System.Linq; 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 @@ namespace NzbDrone.Core.Test.MetadataSource.Goodreads 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(); diff --git a/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs b/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs index 8063e4428..214778b11 100644 --- a/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs +++ b/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs @@ -34,9 +34,13 @@ namespace NzbDrone.Core.Blacklisting return Query(b => b.AuthorId == authorId); } - protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join((b, m) => b.AuthorId == m.Id); - protected override IEnumerable PagedQuery(SqlBuilder builder) => _database.QueryJoined(builder, (bl, author) => + protected override SqlBuilder PagedBuilder() => new SqlBuilder() + .Join((b, m) => b.AuthorId == m.Id) + .Join((l, r) => l.AuthorMetadataId == r.Id); + protected override IEnumerable PagedQuery(SqlBuilder builder) => _database.QueryJoined(builder, + (bl, author, metadata) => { + author.Metadata = metadata; bl.Author = author; return bl; }); diff --git a/src/NzbDrone.Core/Books/Model/Author.cs b/src/NzbDrone.Core/Books/Model/Author.cs index 930261369..c14d70d9d 100644 --- a/src/NzbDrone.Core/Books/Model/Author.cs +++ b/src/NzbDrone.Core/Books/Model/Author.cs @@ -19,7 +19,6 @@ namespace NzbDrone.Core.Books // 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 @@ namespace NzbDrone.Core.Books public override void UseMetadataFrom(Author other) { CleanName = other.CleanName; - SortName = other.SortName; } public override void UseDbFieldsFrom(Author other) diff --git a/src/NzbDrone.Core/Books/Model/AuthorMetadata.cs b/src/NzbDrone.Core/Books/Model/AuthorMetadata.cs index cbac6d7e5..bda86e873 100644 --- a/src/NzbDrone.Core/Books/Model/AuthorMetadata.cs +++ b/src/NzbDrone.Core/Books/Model/AuthorMetadata.cs @@ -19,6 +19,7 @@ namespace NzbDrone.Core.Books public string ForeignAuthorId { get; set; } public string TitleSlug { get; set; } public string Name { get; set; } + public string SortName { get; set; } public List Aliases { get; set; } public string Overview { get; set; } public string Disambiguation { get; set; } @@ -42,6 +43,7 @@ namespace NzbDrone.Core.Books 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; diff --git a/src/NzbDrone.Core/Books/Repositories/BookRepository.cs b/src/NzbDrone.Core/Books/Repositories/BookRepository.cs index 4ea01e1f9..d70c69eb8 100644 --- a/src/NzbDrone.Core/Books/Repositories/BookRepository.cs +++ b/src/NzbDrone.Core/Books/Repositories/BookRepository.cs @@ -91,6 +91,7 @@ namespace NzbDrone.Core.Books #pragma warning disable CS0472 private SqlBuilder BooksWithoutFilesBuilder(DateTime currentTime) => Builder() .Join((l, r) => l.AuthorMetadataId == r.AuthorMetadataId) + .Join((l, r) => l.AuthorMetadataId == r.Id) .Join((b, e) => b.Id == e.BookId) .LeftJoin((t, f) => t.Id == f.EditionId) .Where(f => f.Id == null) @@ -110,6 +111,7 @@ namespace NzbDrone.Core.Books private SqlBuilder BooksWhereCutoffUnmetBuilder(List qualitiesBelowCutoff) => Builder() .Join((l, r) => l.AuthorMetadataId == r.AuthorMetadataId) + .Join((l, r) => l.AuthorMetadataId == r.Id) .Join((b, e) => b.Id == e.BookId) .LeftJoin((t, f) => t.Id == f.EditionId) .Where(e => e.Monitored == true) diff --git a/src/NzbDrone.Core/Books/Services/AddAuthorService.cs b/src/NzbDrone.Core/Books/Services/AddAuthorService.cs index bb222cd3c..005f5d7e0 100644 --- a/src/NzbDrone.Core/Books/Services/AddAuthorService.cs +++ b/src/NzbDrone.Core/Books/Services/AddAuthorService.cs @@ -146,7 +146,6 @@ namespace NzbDrone.Core.Books 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) diff --git a/src/NzbDrone.Core/Datastore/Migration/009_update_author_sort_name.cs b/src/NzbDrone.Core/Datastore/Migration/009_update_author_sort_name.cs new file mode 100644 index 000000000..d1a101fde --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/009_update_author_sort_name.cs @@ -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("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; } + } + } +} diff --git a/src/NzbDrone.Core/History/HistoryRepository.cs b/src/NzbDrone.Core/History/HistoryRepository.cs index 61cddb484..b321afffe 100644 --- a/src/NzbDrone.Core/History/HistoryRepository.cs +++ b/src/NzbDrone.Core/History/HistoryRepository.cs @@ -104,11 +104,13 @@ namespace NzbDrone.Core.History protected override SqlBuilder PagedBuilder() => new SqlBuilder() .Join((h, a) => h.AuthorId == a.Id) + .Join((l, r) => l.AuthorMetadataId == r.Id) .Join((h, a) => h.BookId == a.Id); protected override IEnumerable PagedQuery(SqlBuilder builder) => - _database.QueryJoined(builder, (history, author, book) => + _database.QueryJoined(builder, (history, author, metadata, book) => { + author.Metadata = metadata; history.Author = author; history.Book = book; return history; diff --git a/src/NzbDrone.Core/MetadataSource/Goodreads/GoodreadsProxy.cs b/src/NzbDrone.Core/MetadataSource/Goodreads/GoodreadsProxy.cs index 78a360e0d..a9bf9becc 100644 --- a/src/NzbDrone.Core/MetadataSource/Goodreads/GoodreadsProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/Goodreads/GoodreadsProxy.cs @@ -109,7 +109,6 @@ namespace NzbDrone.Core.MetadataSource.Goodreads 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 @@ namespace NzbDrone.Core.MetadataSource.Goodreads 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 @@ namespace NzbDrone.Core.MetadataSource.Goodreads TitleSlug = resource.Id.ToString() }; + author.SortName = author.Name.ToSortName().ToLower(); + if (resource.RatingsCount.HasValue) { author.Ratings = new Ratings @@ -704,6 +707,7 @@ namespace NzbDrone.Core.MetadataSource.Goodreads { ForeignAuthorId = resource.BestBook.AuthorId.ToString(), Name = resource.BestBook.AuthorName, + SortName = resource.BestBook.AuthorName.ToSortName().ToLower(), TitleSlug = resource.BestBook.AuthorId.ToString() } }; diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 2729c0cb0..f44d2be5f 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -276,7 +276,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook { Metadata = metadata, CleanName = Parser.Parser.CleanAuthorName(metadata.Name), - SortName = Parser.Parser.NormalizeTitle(metadata.Name), Books = books, Series = series }; @@ -316,6 +315,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook 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 diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index 67f3ceb09..699a7e235 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -576,11 +576,6 @@ namespace NzbDrone.Core.Parser 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. diff --git a/src/Readarr.Api.V1/Author/AuthorResource.cs b/src/Readarr.Api.V1/Author/AuthorResource.cs index d0956799f..49c4eaa50 100644 --- a/src/Readarr.Api.V1/Author/AuthorResource.cs +++ b/src/Readarr.Api.V1/Author/AuthorResource.cs @@ -72,7 +72,7 @@ namespace Readarr.Api.V1.Author 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 @@ namespace Readarr.Api.V1.Author 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 @@ namespace Readarr.Api.V1.Author }, //AlternateTitles - SortName = resource.SortName, Path = resource.Path, QualityProfileId = resource.QualityProfileId, MetadataProfileId = resource.MetadataProfileId, diff --git a/src/Readarr.Api.V1/Queue/QueueController.cs b/src/Readarr.Api.V1/Queue/QueueController.cs index 2a4bdf50b..e4c76187e 100644 --- a/src/Readarr.Api.V1/Queue/QueueController.cs +++ b/src/Readarr.Api.V1/Queue/QueueController.cs @@ -172,7 +172,7 @@ namespace Readarr.Api.V1.Queue 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":