mirror of
https://github.com/Readarr/Readarr
synced 2026-05-08 12:42:51 +02:00
parent
7c5188638f
commit
a7c0eabb56
11 changed files with 122 additions and 5 deletions
|
|
@ -79,6 +79,12 @@ const bookTokens = [
|
||||||
|
|
||||||
{ token: '{Book Disambiguation}', example: 'Disambiguation' },
|
{ token: '{Book Disambiguation}', example: 'Disambiguation' },
|
||||||
|
|
||||||
|
{ token: '{Book Series}', example: 'Series Title' },
|
||||||
|
|
||||||
|
{ token: '{Book SeriesPosition}', example: '1' },
|
||||||
|
|
||||||
|
{ token: '{Book SeriesTitle}', example: 'Series Title #1' },
|
||||||
|
|
||||||
{ token: '{PartNumber:0}', example: '2' },
|
{ token: '{PartNumber:0}', example: '2' },
|
||||||
{ token: '{PartNumber:00}', example: '02' },
|
{ token: '{PartNumber:00}', example: '02' },
|
||||||
{ token: '{PartCount:0}', example: '9' },
|
{ token: '{PartCount:0}', example: '9' },
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,23 @@ public void Setup()
|
||||||
.With(s => s.Name = "Avenged Sevenfold")
|
.With(s => s.Name = "Avenged Sevenfold")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
var series = Builder<Series>
|
||||||
|
.CreateNew()
|
||||||
|
.With(x => x.Title = "Series Title")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var seriesLink = Builder<SeriesBookLink>
|
||||||
|
.CreateListOfSize(1)
|
||||||
|
.All()
|
||||||
|
.With(s => s.Position = "1-2")
|
||||||
|
.With(s => s.Series = series)
|
||||||
|
.BuildListOfNew();
|
||||||
|
|
||||||
_book = Builder<Book>
|
_book = Builder<Book>
|
||||||
.CreateNew()
|
.CreateNew()
|
||||||
.With(s => s.Title = "Hail to the King")
|
.With(s => s.Title = "Hail to the King")
|
||||||
.With(s => s.AuthorMetadata = _author.Metadata.Value)
|
.With(s => s.AuthorMetadata = _author.Metadata.Value)
|
||||||
|
.With(s => s.SeriesLinks = seriesLink)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_edition = Builder<Edition>
|
_edition = Builder<Edition>
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,23 @@ public void Setup()
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
var series = Builder<Series>
|
||||||
|
.CreateNew()
|
||||||
|
.With(x => x.Title = "Series Title")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var seriesLink = Builder<SeriesBookLink>
|
||||||
|
.CreateListOfSize(1)
|
||||||
|
.All()
|
||||||
|
.With(s => s.Position = "1-2")
|
||||||
|
.With(s => s.Series = series)
|
||||||
|
.BuildListOfNew();
|
||||||
|
|
||||||
_book = Builder<Book>
|
_book = Builder<Book>
|
||||||
.CreateNew()
|
.CreateNew()
|
||||||
.With(s => s.Title = "Hybrid Theory")
|
.With(s => s.Title = "Hybrid Theory")
|
||||||
.With(s => s.AuthorMetadata = _author.Metadata.Value)
|
.With(s => s.AuthorMetadata = _author.Metadata.Value)
|
||||||
|
.With(s => s.SeriesLinks = seriesLink)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_edition = Builder<Edition>
|
_edition = Builder<Edition>
|
||||||
|
|
@ -247,6 +260,33 @@ public void should_cleanup_Book_Title()
|
||||||
.Should().Be("Hybrid.Theory.2000");
|
.Should().Be("Hybrid.Theory.2000");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_set_series()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardBookFormat = "{Book Series}";
|
||||||
|
|
||||||
|
Subject.BuildBookFileName(_author, _edition, _trackFile)
|
||||||
|
.Should().Be("Series Title");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_set_series_number()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardBookFormat = "{Book SeriesPosition}";
|
||||||
|
|
||||||
|
Subject.BuildBookFileName(_author, _edition, _trackFile)
|
||||||
|
.Should().Be("1-2");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_set_series_title()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardBookFormat = "{Book SeriesTitle}";
|
||||||
|
|
||||||
|
Subject.BuildBookFileName(_author, _edition, _trackFile)
|
||||||
|
.Should().Be("Series Title #1-2");
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_set_part_number()
|
public void should_set_part_number()
|
||||||
{
|
{
|
||||||
|
|
@ -434,7 +474,7 @@ public void should_replace_double_period_with_single_period()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardBookFormat = "{Author.Name}.{Book.Title}";
|
_namingConfig.StandardBookFormat = "{Author.Name}.{Book.Title}";
|
||||||
|
|
||||||
Subject.BuildBookFileName(new Author { Name = "In The Woods." }, new Edition { Title = "30 Rock", Book = new Book { AuthorMetadata = new AuthorMetadata { Name = "Author" } } }, _trackFile)
|
Subject.BuildBookFileName(new Author { Name = "In The Woods." }, new Edition { Title = "30 Rock", Book = new Book { AuthorMetadata = new AuthorMetadata { Name = "Author" }, SeriesLinks = new List<SeriesBookLink>() } }, _trackFile)
|
||||||
.Should().Be("In.The.Woods.30.Rock");
|
.Should().Be("In.The.Woods.30.Rock");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,7 +483,7 @@ public void should_replace_triple_period_with_single_period()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardBookFormat = "{Author.Name}.{Book.Title}";
|
_namingConfig.StandardBookFormat = "{Author.Name}.{Book.Title}";
|
||||||
|
|
||||||
Subject.BuildBookFileName(new Author { Name = "In The Woods..." }, new Edition { Title = "30 Rock", Book = new Book { AuthorMetadata = new AuthorMetadata { Name = "Author" } } }, _trackFile)
|
Subject.BuildBookFileName(new Author { Name = "In The Woods..." }, new Edition { Title = "30 Rock", Book = new Book { AuthorMetadata = new AuthorMetadata { Name = "Author" }, SeriesLinks = new List<SeriesBookLink>() } }, _trackFile)
|
||||||
.Should().Be("In.The.Woods.30.Rock");
|
.Should().Be("In.The.Woods.30.Rock");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,23 @@ public void Setup()
|
||||||
.With(s => s.Name = "Alien Ant Farm")
|
.With(s => s.Name = "Alien Ant Farm")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
var series = Builder<Series>
|
||||||
|
.CreateNew()
|
||||||
|
.With(x => x.Title = "Series Title")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var seriesLink = Builder<SeriesBookLink>
|
||||||
|
.CreateListOfSize(1)
|
||||||
|
.All()
|
||||||
|
.With(s => s.Position = "1-2")
|
||||||
|
.With(s => s.Series = series)
|
||||||
|
.BuildListOfNew();
|
||||||
|
|
||||||
_book = Builder<Book>
|
_book = Builder<Book>
|
||||||
.CreateNew()
|
.CreateNew()
|
||||||
.With(s => s.Title = "Anthology")
|
.With(s => s.Title = "Anthology")
|
||||||
.With(s => s.AuthorMetadata = _author.Metadata.Value)
|
.With(s => s.AuthorMetadata = _author.Metadata.Value)
|
||||||
|
.With(s => s.SeriesLinks = seriesLink)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_edition = Builder<Edition>
|
_edition = Builder<Edition>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ namespace NzbDrone.Core.Books
|
||||||
public class SeriesBookLink : Entity<SeriesBookLink>
|
public class SeriesBookLink : Entity<SeriesBookLink>
|
||||||
{
|
{
|
||||||
public string Position { get; set; }
|
public string Position { get; set; }
|
||||||
|
public int SeriesPosition { get; set; }
|
||||||
public int SeriesId { get; set; }
|
public int SeriesId { get; set; }
|
||||||
public int BookId { get; set; }
|
public int BookId { get; set; }
|
||||||
public bool IsPrimary { get; set; }
|
public bool IsPrimary { get; set; }
|
||||||
|
|
@ -18,6 +19,7 @@ public class SeriesBookLink : Entity<SeriesBookLink>
|
||||||
public override void UseMetadataFrom(SeriesBookLink other)
|
public override void UseMetadataFrom(SeriesBookLink other)
|
||||||
{
|
{
|
||||||
Position = other.Position;
|
Position = other.Position;
|
||||||
|
SeriesPosition = other.SeriesPosition;
|
||||||
IsPrimary = other.IsPrimary;
|
IsPrimary = other.IsPrimary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using FluentMigrator;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(18)]
|
||||||
|
public class AddSeriesPosition : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Alter.Table("SeriesBookLink").AddColumn("SeriesPosition").AsInt32().WithDefaultValue(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -196,7 +196,8 @@ private static void MapSeriesLinks(List<Series> series, List<Book> books, Author
|
||||||
Book = bookDict[l.ForeignWorkId.ToString()],
|
Book = bookDict[l.ForeignWorkId.ToString()],
|
||||||
Series = curr,
|
Series = curr,
|
||||||
IsPrimary = l.Primary,
|
IsPrimary = l.Primary,
|
||||||
Position = l.PositionInSeries
|
Position = l.PositionInSeries,
|
||||||
|
SeriesPosition = l.SeriesPosition
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,17 @@ private void AddBookTokens(Dictionary<string, Func<TokenMatch, string>> tokenHan
|
||||||
tokenHandlers["{Book CleanSubtitle}"] = m => CleanTitle(subtitle);
|
tokenHandlers["{Book CleanSubtitle}"] = m => CleanTitle(subtitle);
|
||||||
tokenHandlers["{Book SubtitleThe}"] = m => TitleThe(subtitle);
|
tokenHandlers["{Book SubtitleThe}"] = m => TitleThe(subtitle);
|
||||||
|
|
||||||
|
var seriesLinks = edition.Book.Value.SeriesLinks.Value;
|
||||||
|
if (seriesLinks.Any())
|
||||||
|
{
|
||||||
|
var primarySeries = seriesLinks.OrderBy(x => x.SeriesPosition).First();
|
||||||
|
var seriesTitle = primarySeries.Series?.Value?.Title + (primarySeries.Position.IsNotNullOrWhiteSpace() ? $" #{primarySeries.Position}" : string.Empty);
|
||||||
|
|
||||||
|
tokenHandlers["{Book Series}"] = m => primarySeries.Series.Value.Title;
|
||||||
|
tokenHandlers["{Book SeriesPosition}"] = m => primarySeries.Position;
|
||||||
|
tokenHandlers["{Book SeriesTitle}"] = m => seriesTitle;
|
||||||
|
}
|
||||||
|
|
||||||
if (edition.Disambiguation != null)
|
if (edition.Disambiguation != null)
|
||||||
{
|
{
|
||||||
tokenHandlers["{Book Disambiguation}"] = m => edition.Disambiguation;
|
tokenHandlers["{Book Disambiguation}"] = m => edition.Disambiguation;
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,24 @@ public FileNameSampleService(IBuildFileNames buildFileNames)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var series = new Series
|
||||||
|
{
|
||||||
|
Title = "Series Title"
|
||||||
|
};
|
||||||
|
|
||||||
|
var seriesLink = new SeriesBookLink
|
||||||
|
{
|
||||||
|
Position = "1",
|
||||||
|
Series = series
|
||||||
|
};
|
||||||
|
|
||||||
_standardBook = new Book
|
_standardBook = new Book
|
||||||
{
|
{
|
||||||
Title = "The Book Title",
|
Title = "The Book Title",
|
||||||
ReleaseDate = System.DateTime.Today,
|
ReleaseDate = System.DateTime.Today,
|
||||||
Author = _standardAuthor,
|
Author = _standardAuthor,
|
||||||
AuthorMetadata = _standardAuthor.Metadata.Value
|
AuthorMetadata = _standardAuthor.Metadata.Value,
|
||||||
|
SeriesLinks = new List<SeriesBookLink> { seriesLink }
|
||||||
};
|
};
|
||||||
|
|
||||||
_standardEdition = new Edition
|
_standardEdition = new Edition
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ public static BookResource ToResource(this Book model)
|
||||||
var title = selectedEdition?.Title ?? model.Title;
|
var title = selectedEdition?.Title ?? model.Title;
|
||||||
var authorTitle = $"{model.Author?.Value?.Metadata?.Value?.SortNameLastFirst} {title}";
|
var authorTitle = $"{model.Author?.Value?.Metadata?.Value?.SortNameLastFirst} {title}";
|
||||||
|
|
||||||
|
var seriesLinks = model.SeriesLinks?.Value?.OrderBy(x => x.SeriesPosition);
|
||||||
|
var seriesTitle = seriesLinks?.Select(x => x?.Series?.Value?.Title + (x?.Position.IsNotNullOrWhiteSpace() ?? false ? $" #{x.Position}" : string.Empty)).ConcatToString("; ");
|
||||||
|
|
||||||
return new BookResource
|
return new BookResource
|
||||||
{
|
{
|
||||||
Id = model.Id,
|
Id = model.Id,
|
||||||
|
|
@ -67,7 +70,7 @@ public static BookResource ToResource(this Book model)
|
||||||
Genres = model.Genres,
|
Genres = model.Genres,
|
||||||
Title = title,
|
Title = title,
|
||||||
AuthorTitle = authorTitle,
|
AuthorTitle = authorTitle,
|
||||||
SeriesTitle = model.SeriesLinks?.Value?.Select(x => x?.Series?.Value?.Title + (x?.Position.IsNotNullOrWhiteSpace() ?? false ? $" #{x.Position}" : string.Empty)).ConcatToString("; "),
|
SeriesTitle = seriesTitle,
|
||||||
Disambiguation = selectedEdition?.Disambiguation,
|
Disambiguation = selectedEdition?.Disambiguation,
|
||||||
Overview = selectedEdition?.Overview,
|
Overview = selectedEdition?.Overview,
|
||||||
Images = selectedEdition?.Images ?? new List<MediaCover>(),
|
Images = selectedEdition?.Images ?? new List<MediaCover>(),
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ namespace Readarr.Api.V1.Series
|
||||||
public class SeriesBookLinkResource : RestResource
|
public class SeriesBookLinkResource : RestResource
|
||||||
{
|
{
|
||||||
public string Position { get; set; }
|
public string Position { get; set; }
|
||||||
|
public int SeriesPosition { get; set; }
|
||||||
public int SeriesId { get; set; }
|
public int SeriesId { get; set; }
|
||||||
public int BookId { get; set; }
|
public int BookId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -20,6 +21,7 @@ public static SeriesBookLinkResource ToResource(this SeriesBookLink model)
|
||||||
{
|
{
|
||||||
Id = model.Id,
|
Id = model.Id,
|
||||||
Position = model.Position,
|
Position = model.Position,
|
||||||
|
SeriesPosition = model.SeriesPosition,
|
||||||
SeriesId = model.SeriesId,
|
SeriesId = model.SeriesId,
|
||||||
BookId = model.BookId
|
BookId = model.BookId
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue