New: Add support for SortName in artist naming tokens

This commit is contained in:
Brock Grassy 2025-11-25 15:14:23 -05:00
parent d8f79c0189
commit c27d8c2fcb
5 changed files with 185 additions and 0 deletions

View file

@ -75,10 +75,13 @@ const fileNameTokens = [
const artistTokens = [
{ token: '{Artist Name}', example: 'Artist Name' },
{ token: '{Artist SortName}', example: 'Artist LastName, Artist FirstName'},
{ token: '{Artist CleanName}', example: 'Artist Name' },
{ token: '{Artist CleanSortName}', example: 'Artist LastName, Artist FirstName'},
{ token: '{Artist NameThe}', example: 'Artist Name, The' },
{ token: '{Artist CleanNameThe}', example: 'Artist Name, The' },
{ token: '{Artist NameFirstCharacter}', example: 'A' },
{ token: '{Artist SortNameFirstCharacter}', example: 'A' },
{ token: '{Artist Disambiguation}', example: 'Disambiguation' },
{ token: '{Artist Genre}', example: 'Pop' },
{ token: '{Artist MbId}', example: 'db92a151-1ac2-438b-bc43-b82e149ddd50' }

View file

@ -0,0 +1,56 @@
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Music;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{
[TestFixture]
public class ArtistCleanSortNameFixture : CoreTest<FileNameBuilder>
{
private Artist _artist;
private NamingConfig _namingConfig;
[SetUp]
public void Setup()
{
_artist = Builder<Artist>
.CreateNew()
.Build();
_namingConfig = NamingConfig.Default;
_namingConfig.RenameTracks = true;
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
Mocker.GetMock<IQualityDefinitionService>()
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
}
[TestCase("AC/DC", "AC DC")]
[TestCase("Guns N' Roses", "Guns N Roses")]
[TestCase("Twenty Øne Piløts", "Twenty One Pilots")]
public void should_get_expected_folder_name_back(string sortName, string cleanSortName)
{
_artist.SortName = sortName;
_namingConfig.ArtistFolderFormat = "{Artist CleanSortName}";
Subject.GetArtistFolder(_artist).Should().Be(cleanSortName);
}
[Test]
public void should_be_able_to_use_lower_case_clean_sort_name()
{
_artist.SortName = "AC/DC";
_namingConfig.ArtistFolderFormat = "{artist cleansortname}";
Subject.GetArtistFolder(_artist).Should().Be("ac dc");
}
}
}

View file

@ -0,0 +1,63 @@
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Music;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{
[TestFixture]
public class ArtistSortNameFirstCharacterFixture : CoreTest<FileNameBuilder>
{
private Artist _artist;
private NamingConfig _namingConfig;
[SetUp]
public void Setup()
{
_artist = Builder<Artist>
.CreateNew()
.Build();
_namingConfig = NamingConfig.Default;
_namingConfig.RenameTracks = true;
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
Mocker.GetMock<IQualityDefinitionService>()
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
}
[TestCase("Beatles, The", "B", "Beatles, The")]
[TestCase("1975, The", "1", "1975, The")]
[TestCase("Smith, Elliott", "S", "Smith, Elliott")]
[TestCase("Madonna", "M", "Madonna")]
[TestCase("Perfect Circle, A", "P", "Perfect Circle, A")]
[TestCase("Rolling Stones, The", "R", "Rolling Stones, The")]
[TestCase("50 Cent", "5", "50 Cent")]
[TestCase("¡Forward, Russia!", "F", "¡Forward, Russia!")]
[TestCase(".hack//SIGN", "H", "hack++SIGN")]
public void should_get_expected_folder_name_back(string sortName, string parent, string child)
{
_artist.SortName = sortName;
_namingConfig.ArtistFolderFormat = "{Artist SortNameFirstCharacter}\\{Artist SortName}";
Subject.GetArtistFolder(_artist).Should().Be(Path.Combine(parent, child));
}
[Test]
public void should_be_able_to_use_lower_case_first_character()
{
_artist.SortName = "Beatles, The";
_namingConfig.ArtistFolderFormat = "{artist sortnamefirstcharacter}\\{artist sortname}";
Subject.GetArtistFolder(_artist).Should().Be(Path.Combine("b", "beatles, the"));
}
}
}

View file

@ -0,0 +1,60 @@
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Music;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{
[TestFixture]
public class ArtistSortNameFixture : CoreTest<FileNameBuilder>
{
private Artist _artist;
private NamingConfig _namingConfig;
[SetUp]
public void Setup()
{
_artist = Builder<Artist>
.CreateNew()
.Build();
_namingConfig = NamingConfig.Default;
_namingConfig.RenameTracks = true;
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
Mocker.GetMock<IQualityDefinitionService>()
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
}
[TestCase("The Beatles", "Beatles, The")]
[TestCase("Elliott Smith", "Smith, Elliott")]
[TestCase("Madonna", "Madonna")]
[TestCase("A Perfect Circle", "Perfect Circle, A")]
[TestCase("The Rolling Stones", "Rolling Stones, The")]
public void should_get_expected_folder_name_back(string name, string sortName)
{
_artist.Name = name;
_artist.SortName = sortName;
_namingConfig.ArtistFolderFormat = "{Artist SortName}";
Subject.GetArtistFolder(_artist).Should().Be(sortName);
}
[Test]
public void should_be_able_to_use_lower_case_sort_name()
{
_artist.Name = "The Beatles";
_artist.SortName = "Beatles, The";
_namingConfig.ArtistFolderFormat = "{artist sortname}";
Subject.GetArtistFolder(_artist).Should().Be("beatles, the");
}
}
}

View file

@ -310,10 +310,13 @@ private void AddArtistTokens(Dictionary<string, Func<TokenMatch, string>> tokenH
{
tokenHandlers["{Artist Name}"] = m => Truncate(artist.Name, m.CustomFormat);
tokenHandlers["{Artist CleanName}"] = m => Truncate(CleanTitle(artist.Name), m.CustomFormat);
tokenHandlers["{Artist SortName}"] = m => Truncate(artist.SortName, m.CustomFormat);
tokenHandlers["{Artist CleanSortName}"] = m => Truncate(CleanTitle(artist.SortName), m.CustomFormat);
tokenHandlers["{Artist NameThe}"] = m => Truncate(TitleThe(artist.Name), m.CustomFormat);
tokenHandlers["{Artist CleanNameThe}"] = m => Truncate(CleanTitleThe(artist.Name), m.CustomFormat);
tokenHandlers["{Artist Genre}"] = m => artist.Metadata.Value.Genres?.FirstOrDefault() ?? string.Empty;
tokenHandlers["{Artist NameFirstCharacter}"] = m => TitleFirstCharacter(TitleThe(artist.Name));
tokenHandlers["{Artist SortNameFirstCharacter}"] = m => TitleFirstCharacter(artist.SortName);
tokenHandlers["{Artist MbId}"] = m => artist.ForeignArtistId ?? string.Empty;
if (artist.Metadata.Value.Disambiguation != null)