mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Float-based Volumes (#2659)
This commit is contained in:
parent
6fdc9228df
commit
f6af6d66be
@ -25,7 +25,7 @@ public class TestBenchmark
|
||||
{
|
||||
list.Add(new VolumeDto()
|
||||
{
|
||||
Number = random.Next(10) > 5 ? 1 : 0,
|
||||
MinNumber = random.Next(10) > 5 ? 1 : 0,
|
||||
Chapters = GenerateChapters()
|
||||
});
|
||||
}
|
||||
@ -49,7 +49,7 @@ public class TestBenchmark
|
||||
|
||||
private static void SortSpecialChapters(IEnumerable<VolumeDto> volumes)
|
||||
{
|
||||
foreach (var v in volumes.Where(vDto => vDto.Number == 0))
|
||||
foreach (var v in volumes.Where(vDto => vDto.MinNumber == 0))
|
||||
{
|
||||
v.Chapters = v.Chapters.OrderByNatural(x => x.Range).ToList();
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ public class SeriesExtensionsTests
|
||||
.Build())
|
||||
.Build())
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithIsSpecial(false)
|
||||
.WithCoverImage("Volume 1")
|
||||
@ -229,7 +229,7 @@ public class SeriesExtensionsTests
|
||||
.Build())
|
||||
.Build())
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithIsSpecial(false)
|
||||
.WithCoverImage("Volume 1")
|
||||
@ -266,14 +266,14 @@ public class SeriesExtensionsTests
|
||||
.Build())
|
||||
.Build())
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithIsSpecial(false)
|
||||
.WithCoverImage("Volume 1")
|
||||
.Build())
|
||||
.Build())
|
||||
.WithVolume(new VolumeBuilder("137")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithIsSpecial(false)
|
||||
.WithCoverImage("Volume 137")
|
||||
@ -306,7 +306,7 @@ public class SeriesExtensionsTests
|
||||
.Build())
|
||||
.Build())
|
||||
.WithVolume(new VolumeBuilder("4")
|
||||
.WithNumber(4)
|
||||
.WithMinNumber(4)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithIsSpecial(false)
|
||||
.WithCoverImage("Volume 4")
|
||||
|
@ -27,7 +27,7 @@ public class VolumeListExtensionsTests
|
||||
.Build(),
|
||||
};
|
||||
|
||||
Assert.Equal(volumes[0].Number, volumes.GetCoverImage(MangaFormat.Archive).Number);
|
||||
Assert.Equal(volumes[0].MinNumber, volumes.GetCoverImage(MangaFormat.Archive).MinNumber);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -180,7 +180,7 @@ Substitute.For<IMediaConversionService>());
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithFormat(MangaFormat.Epub)
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.Build())
|
||||
.Build())
|
||||
@ -246,7 +246,7 @@ Substitute.For<IMediaConversionService>());
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithFormat(MangaFormat.Epub)
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1")
|
||||
.Build())
|
||||
.Build())
|
||||
@ -322,7 +322,7 @@ Substitute.For<IMediaConversionService>());
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithFormat(MangaFormat.Epub)
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1")
|
||||
.Build())
|
||||
.Build())
|
||||
@ -375,7 +375,7 @@ Substitute.For<IMediaConversionService>());
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithFormat(MangaFormat.Epub)
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1")
|
||||
.Build())
|
||||
.Build())
|
||||
@ -428,7 +428,7 @@ Substitute.For<IMediaConversionService>());
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithFormat(MangaFormat.Epub)
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1")
|
||||
.Build())
|
||||
.Build())
|
||||
|
@ -395,7 +395,7 @@ public class CleanupServiceTests : AbstractDbTest
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithFormat(MangaFormat.Epub)
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(c)
|
||||
.Build())
|
||||
.Build();
|
||||
|
@ -136,7 +136,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithPages(1)
|
||||
.Build())
|
||||
@ -166,7 +166,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithPages(1)
|
||||
.Build())
|
||||
@ -205,7 +205,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithPages(1)
|
||||
.Build())
|
||||
@ -260,7 +260,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithPages(1)
|
||||
.Build())
|
||||
@ -299,7 +299,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("0")
|
||||
.WithPages(1)
|
||||
.Build())
|
||||
@ -347,19 +347,19 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("2")
|
||||
.WithNumber(2)
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("21").Build())
|
||||
.WithChapter(new ChapterBuilder("22").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("3")
|
||||
.WithNumber(3)
|
||||
.WithMinNumber(3)
|
||||
.WithChapter(new ChapterBuilder("31").Build())
|
||||
.WithChapter(new ChapterBuilder("32").Build())
|
||||
.Build())
|
||||
@ -382,7 +382,7 @@ public class ReaderServiceTests
|
||||
Assert.Equal("2", actualChapter.Range);
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
[Fact]
|
||||
public async Task GetNextChapterIdAsync_ShouldGetNextVolume_WhenUsingRanges()
|
||||
{
|
||||
// V1 -> V2
|
||||
@ -390,15 +390,13 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1-2")
|
||||
.WithNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("0").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("3-4")
|
||||
.WithNumber(2)
|
||||
.WithChapter(new ChapterBuilder("21").Build())
|
||||
.WithChapter(new ChapterBuilder("22").Build())
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.Build())
|
||||
.Build();
|
||||
series.Library = new LibraryBuilder("Test Lib", LibraryType.Manga).Build();
|
||||
@ -414,7 +412,8 @@ public class ReaderServiceTests
|
||||
|
||||
var nextChapter = await _readerService.GetNextChapterIdAsync(1, 1, 1, 1);
|
||||
var actualChapter = await _unitOfWork.ChapterRepository.GetChapterAsync(nextChapter);
|
||||
Assert.Equal("3-4", actualChapter.Range);
|
||||
Assert.Equal("3-4", actualChapter.Volume.Name);
|
||||
Assert.Equal("1", actualChapter.Range);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -467,19 +466,19 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("2")
|
||||
.WithNumber(2)
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("21").Build())
|
||||
.WithChapter(new ChapterBuilder("22").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("3")
|
||||
.WithNumber(3)
|
||||
.WithMinNumber(3)
|
||||
.WithChapter(new ChapterBuilder("31").Build())
|
||||
.WithChapter(new ChapterBuilder("32").Build())
|
||||
.Build())
|
||||
@ -508,19 +507,19 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("1.5")
|
||||
.WithNumber(2)
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("21").Build())
|
||||
.WithChapter(new ChapterBuilder("22").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("3")
|
||||
.WithNumber(3)
|
||||
.WithMinNumber(3)
|
||||
.WithChapter(new ChapterBuilder("31").Build())
|
||||
.WithChapter(new ChapterBuilder("32").Build())
|
||||
.Build())
|
||||
@ -550,13 +549,13 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("21").Build())
|
||||
.WithChapter(new ChapterBuilder("22").Build())
|
||||
.Build())
|
||||
@ -585,18 +584,18 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("66").Build())
|
||||
.WithChapter(new ChapterBuilder("67").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("2")
|
||||
.WithNumber(2)
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("0").Build())
|
||||
.Build())
|
||||
.Build();
|
||||
@ -627,13 +626,13 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("A.cbz").WithIsSpecial(true).Build())
|
||||
.WithChapter(new ChapterBuilder("B.cbz").WithIsSpecial(true).Build())
|
||||
.Build())
|
||||
@ -659,7 +658,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
@ -685,7 +684,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
@ -712,14 +711,14 @@ public class ReaderServiceTests
|
||||
//
|
||||
// var series = new SeriesBuilder("Test")
|
||||
// .WithVolume(new VolumeBuilder("0")
|
||||
// .WithNumber(0)
|
||||
// .WithMinNumber(0)
|
||||
// .WithChapter(new ChapterBuilder("1").Build())
|
||||
// .WithChapter(new ChapterBuilder("2").Build())
|
||||
// .WithChapter(new ChapterBuilder("0").WithIsSpecial(true).Build())
|
||||
// .Build())
|
||||
//
|
||||
// .WithVolume(new VolumeBuilder("1")
|
||||
// .WithNumber(1)
|
||||
// .WithMinNumber(1)
|
||||
// .WithChapter(new ChapterBuilder("2").Build())
|
||||
// .Build())
|
||||
// .Build();
|
||||
@ -746,13 +745,13 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("A.cbz").WithIsSpecial(true).Build())
|
||||
.WithChapter(new ChapterBuilder("B.cbz").WithIsSpecial(true).Build())
|
||||
.Build())
|
||||
@ -782,7 +781,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("A.cbz").WithIsSpecial(true).Build())
|
||||
@ -813,13 +812,13 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("A.cbz").WithIsSpecial(true).Build())
|
||||
.Build())
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("0").Build())
|
||||
.Build())
|
||||
.Build();
|
||||
@ -846,12 +845,12 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("A.cbz").WithIsSpecial(true).Build())
|
||||
.WithChapter(new ChapterBuilder("B.cbz").WithIsSpecial(true).Build())
|
||||
.Build())
|
||||
@ -881,12 +880,12 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("12").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("2")
|
||||
.WithNumber(2)
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("12").Build())
|
||||
.Build())
|
||||
.Build();
|
||||
@ -907,7 +906,7 @@ public class ReaderServiceTests
|
||||
|
||||
var nextChapter = await _readerService.GetNextChapterIdAsync(1, 1, 1, 1);
|
||||
var actualChapter = await _unitOfWork.ChapterRepository.GetChapterAsync(nextChapter, ChapterIncludes.Volumes);
|
||||
Assert.Equal(2, actualChapter.Volume.Number);
|
||||
Assert.Equal(2, actualChapter.Volume.MinNumber);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -922,19 +921,19 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("2")
|
||||
.WithNumber(2)
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("21").Build())
|
||||
.WithChapter(new ChapterBuilder("22").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("3")
|
||||
.WithNumber(3)
|
||||
.WithMinNumber(3)
|
||||
.WithChapter(new ChapterBuilder("31").Build())
|
||||
.WithChapter(new ChapterBuilder("32").Build())
|
||||
.Build())
|
||||
@ -965,19 +964,19 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("1.5")
|
||||
.WithNumber(2)
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("21").Build())
|
||||
.WithChapter(new ChapterBuilder("22").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("3")
|
||||
.WithNumber(3)
|
||||
.WithMinNumber(3)
|
||||
.WithChapter(new ChapterBuilder("31").Build())
|
||||
.WithChapter(new ChapterBuilder("32").Build())
|
||||
.Build())
|
||||
@ -1089,13 +1088,13 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("A.cbz").WithIsSpecial(true).Build())
|
||||
.WithChapter(new ChapterBuilder("B.cbz").WithIsSpecial(true).Build())
|
||||
.Build())
|
||||
@ -1127,7 +1126,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
@ -1157,7 +1156,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("0").Build())
|
||||
.Build())
|
||||
.Build();
|
||||
@ -1186,13 +1185,13 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("0").Build())
|
||||
.Build())
|
||||
.Build();
|
||||
@ -1221,20 +1220,20 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("5").Build())
|
||||
.WithChapter(new ChapterBuilder("6").Build())
|
||||
.WithChapter(new ChapterBuilder("7").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").WithIsSpecial(true).Build())
|
||||
.WithChapter(new ChapterBuilder("2").WithIsSpecial(true).Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("2")
|
||||
.WithNumber(2)
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("3").WithIsSpecial(true).Build())
|
||||
.WithChapter(new ChapterBuilder("4").WithIsSpecial(true).Build())
|
||||
.Build())
|
||||
@ -1269,7 +1268,7 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
@ -1299,12 +1298,12 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("A.cbz").WithIsSpecial(true).Build())
|
||||
.WithChapter(new ChapterBuilder("B.cbz").WithIsSpecial(true).Build())
|
||||
.Build())
|
||||
@ -1337,12 +1336,12 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("0")
|
||||
.WithNumber(0)
|
||||
.WithMinNumber(0)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.Build())
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("21").Build())
|
||||
.WithChapter(new ChapterBuilder("22").Build())
|
||||
.Build())
|
||||
@ -1375,12 +1374,12 @@ public class ReaderServiceTests
|
||||
|
||||
var series = new SeriesBuilder("Test")
|
||||
.WithVolume(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("12").Build())
|
||||
.Build())
|
||||
|
||||
.WithVolume(new VolumeBuilder("2")
|
||||
.WithNumber(2)
|
||||
.WithMinNumber(2)
|
||||
.WithChapter(new ChapterBuilder("12").Build())
|
||||
.Build())
|
||||
.Build();
|
||||
@ -1396,7 +1395,7 @@ public class ReaderServiceTests
|
||||
|
||||
var nextChapter = await _readerService.GetPrevChapterIdAsync(1, 2, 2, 1);
|
||||
var actualChapter = await _unitOfWork.ChapterRepository.GetChapterAsync(nextChapter, ChapterIncludes.Volumes);
|
||||
Assert.Equal(1, actualChapter.Volume.Number);
|
||||
Assert.Equal(1, actualChapter.Volume.MinNumber);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -759,7 +759,7 @@ public class ReadingListServiceTests
|
||||
var fablesSeries = new SeriesBuilder("Fables").Build();
|
||||
fablesSeries.Volumes.Add(
|
||||
new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithName("2002")
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.Build()
|
||||
@ -937,7 +937,7 @@ public class ReadingListServiceTests
|
||||
var fables2Series = new SeriesBuilder("Fables: The Last Castle").Build();
|
||||
|
||||
fablesSeries.Volumes.Add(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithName("2002")
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
@ -945,7 +945,7 @@ public class ReadingListServiceTests
|
||||
.Build()
|
||||
);
|
||||
fables2Series.Volumes.Add(new VolumeBuilder("1")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithName("2003")
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
@ -980,13 +980,13 @@ public class ReadingListServiceTests
|
||||
var fables2Series = new SeriesBuilder("Fablesa: The Last Castle").Build();
|
||||
|
||||
fablesSeries.Volumes.Add(new VolumeBuilder("2002")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("3").Build())
|
||||
.Build());
|
||||
fables2Series.Volumes.Add(new VolumeBuilder("2003")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("3").Build())
|
||||
@ -1036,7 +1036,7 @@ public class ReadingListServiceTests
|
||||
// Mock up our series
|
||||
var fablesSeries = new SeriesBuilder("Fables")
|
||||
.WithVolume(new VolumeBuilder("2002")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("3").Build())
|
||||
@ -1045,7 +1045,7 @@ public class ReadingListServiceTests
|
||||
|
||||
var fables2Series = new SeriesBuilder("Fables: The Last Castle")
|
||||
.WithVolume(new VolumeBuilder("2003")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("3").Build())
|
||||
@ -1094,13 +1094,13 @@ public class ReadingListServiceTests
|
||||
var fables2Series = new SeriesBuilder("Fables: The Last Castle").Build();
|
||||
|
||||
fablesSeries.Volumes.Add(new VolumeBuilder("2002")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("3").Build())
|
||||
.Build());
|
||||
fables2Series.Volumes.Add(new VolumeBuilder("2003")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("3").Build())
|
||||
@ -1153,13 +1153,13 @@ public class ReadingListServiceTests
|
||||
var fables2Series = new SeriesBuilder("Fables: The Last Castle").Build();
|
||||
|
||||
fablesSeries.Volumes.Add(new VolumeBuilder("2002")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("3").Build())
|
||||
.Build());
|
||||
fables2Series.Volumes.Add(new VolumeBuilder("2003")
|
||||
.WithNumber(1)
|
||||
.WithMinNumber(1)
|
||||
.WithChapter(new ChapterBuilder("1").Build())
|
||||
.WithChapter(new ChapterBuilder("2").Build())
|
||||
.WithChapter(new ChapterBuilder("3").Build())
|
||||
|
@ -95,7 +95,7 @@
|
||||
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
|
||||
<PackageReference Include="SharpCompress" Version="0.36.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.17.0.82934">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.18.0.83559">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -103,7 +103,7 @@ public class DownloadController : BaseApiController
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(volume.SeriesId);
|
||||
try
|
||||
{
|
||||
return await DownloadFiles(files, $"download_{User.GetUsername()}_v{volumeId}", $"{series!.Name} - Volume {volume.Number}.zip");
|
||||
return await DownloadFiles(files, $"download_{User.GetUsername()}_v{volumeId}", $"{series!.Name} - Volume {volume.Name}.zip");
|
||||
}
|
||||
catch (KavitaException ex)
|
||||
{
|
||||
|
@ -1109,7 +1109,7 @@ public class OpdsController : BaseApiController
|
||||
title += $" - {volume.Name}";
|
||||
}
|
||||
}
|
||||
else if (volume.Number != 0)
|
||||
else if (volume.MinNumber != 0)
|
||||
{
|
||||
title = $"{series.Name} - Volume {volume.Name} - {await _seriesService.FormatChapterTitle(userId, chapter, libraryType)}";
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ namespace API.DTOs.SeriesDetail;
|
||||
public class NextExpectedChapterDto
|
||||
{
|
||||
public float ChapterNumber { get; set; }
|
||||
public int VolumeNumber { get; set; }
|
||||
public float VolumeNumber { get; set; }
|
||||
/// <summary>
|
||||
/// Null if not applicable
|
||||
/// </summary>
|
||||
|
@ -9,9 +9,10 @@ namespace API.DTOs;
|
||||
public class VolumeDto : IHasReadTimeEstimate
|
||||
{
|
||||
public int Id { get; set; }
|
||||
/// <inheritdoc cref="Volume.Number"/>
|
||||
public int Number { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Volume.MinNumber"/>
|
||||
public float MinNumber { get; set; }
|
||||
/// <inheritdoc cref="Volume.MaxNumber"/>
|
||||
public float MaxNumber { get; set; }
|
||||
/// <inheritdoc cref="Volume.Name"/>
|
||||
public string Name { get; set; } = default!;
|
||||
public int Pages { get; set; }
|
||||
|
38
API/Data/ManualMigrations/MigrateVolumeNumber.cs
Normal file
38
API/Data/ManualMigrations/MigrateVolumeNumber.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.Enums;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data.ManualMigrations;
|
||||
|
||||
/// <summary>
|
||||
/// Introduced in v0.7.14, this migrates the existing Volume Name -> Volume Min/Max Number
|
||||
/// </summary>
|
||||
public static class MigrateVolumeNumber
|
||||
{
|
||||
public static async Task Migrate(IUnitOfWork unitOfWork, DataContext dataContext, ILogger<Program> logger)
|
||||
{
|
||||
logger.LogCritical(
|
||||
"Running MigrateVolumeNumber migration - Please be patient, this may take some time. This is not an error");
|
||||
if (await dataContext.Volume.AnyAsync(v => v.MaxNumber > 0))
|
||||
{
|
||||
logger.LogCritical(
|
||||
"Running MigrateVolumeNumber migration - Completed. This is not an error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all volumes
|
||||
foreach (var volume in dataContext.Volume)
|
||||
{
|
||||
volume.MinNumber = Parser.MinNumberFromRange(volume.Name);
|
||||
volume.MaxNumber = Parser.MaxNumberFromRange(volume.Name);
|
||||
}
|
||||
|
||||
await dataContext.SaveChangesAsync();
|
||||
logger.LogCritical(
|
||||
"Running MigrateVolumeNumber migration - Completed. This is not an error");
|
||||
}
|
||||
}
|
2793
API/Data/Migrations/20240128153433_VolumeMinMaxNumbers.Designer.cs
generated
Normal file
2793
API/Data/Migrations/20240128153433_VolumeMinMaxNumbers.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
40
API/Data/Migrations/20240128153433_VolumeMinMaxNumbers.cs
Normal file
40
API/Data/Migrations/20240128153433_VolumeMinMaxNumbers.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class VolumeMinMaxNumbers : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<float>(
|
||||
name: "MaxNumber",
|
||||
table: "Volume",
|
||||
type: "REAL",
|
||||
nullable: false,
|
||||
defaultValue: 0f);
|
||||
|
||||
migrationBuilder.AddColumn<float>(
|
||||
name: "MinNumber",
|
||||
table: "Volume",
|
||||
type: "REAL",
|
||||
nullable: false,
|
||||
defaultValue: 0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MaxNumber",
|
||||
table: "Volume");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MinNumber",
|
||||
table: "Volume");
|
||||
}
|
||||
}
|
||||
}
|
@ -1781,9 +1781,15 @@ namespace API.Data.Migrations
|
||||
b.Property<int>("MaxHoursToRead")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("MaxNumber")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("MinHoursToRead")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("MinNumber")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
@ -18,6 +18,6 @@ public class RecentlyAddedSeries
|
||||
public string? ChapterRange { get; init; }
|
||||
public string? ChapterTitle { get; init; }
|
||||
public bool IsSpecial { get; init; }
|
||||
public int VolumeNumber { get; init; }
|
||||
public float VolumeNumber { get; init; }
|
||||
public AgeRating AgeRating { get; init; }
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public interface IAppUserProgressRepository
|
||||
Task<ProgressDto?> GetUserProgressDtoAsync(int chapterId, int userId);
|
||||
Task<bool> AnyUserProgressForSeriesAsync(int seriesId, int userId);
|
||||
Task<int> GetHighestFullyReadChapterForSeries(int seriesId, int userId);
|
||||
Task<int> GetHighestFullyReadVolumeForSeries(int seriesId, int userId);
|
||||
Task<float> GetHighestFullyReadVolumeForSeries(int seriesId, int userId);
|
||||
Task<DateTime?> GetLatestProgressForSeries(int seriesId, int userId);
|
||||
Task<DateTime?> GetFirstProgressForSeries(int seriesId, int userId);
|
||||
Task UpdateAllProgressThatAreMoreThanChapterPages();
|
||||
@ -172,14 +172,14 @@ public class AppUserProgressRepository : IAppUserProgressRepository
|
||||
return list.Count == 0 ? 0 : list.DefaultIfEmpty().Where(d => d != null).Max(d => (int) Math.Floor(Parser.MaxNumberFromRange(d)));
|
||||
}
|
||||
|
||||
public async Task<int> GetHighestFullyReadVolumeForSeries(int seriesId, int userId)
|
||||
public async Task<float> GetHighestFullyReadVolumeForSeries(int seriesId, int userId)
|
||||
{
|
||||
var list = await _context.AppUserProgresses
|
||||
.Join(_context.Chapter, appUserProgresses => appUserProgresses.ChapterId, chapter => chapter.Id,
|
||||
(appUserProgresses, chapter) => new {appUserProgresses, chapter})
|
||||
.Where(p => p.appUserProgresses.SeriesId == seriesId && p.appUserProgresses.AppUserId == userId &&
|
||||
p.appUserProgresses.PagesRead >= p.chapter.Pages)
|
||||
.Select(p => p.chapter.Volume.Number)
|
||||
.Select(p => p.chapter.Volume.MaxNumber)
|
||||
.ToListAsync();
|
||||
return list.Count == 0 ? 0 : list.DefaultIfEmpty().Max();
|
||||
}
|
||||
|
@ -1836,7 +1836,7 @@ public class SeriesRepository : ISeriesRepository
|
||||
ChapterNumber = c.Number,
|
||||
ChapterRange = c.Range,
|
||||
IsSpecial = c.IsSpecial,
|
||||
VolumeNumber = c.Volume.Number,
|
||||
VolumeNumber = c.Volume.MinNumber,
|
||||
ChapterTitle = c.Title,
|
||||
AgeRating = c.Volume.Series.Metadata.AgeRating
|
||||
})
|
||||
|
@ -152,7 +152,7 @@ public class VolumeRepository : IVolumeRepository
|
||||
.Include(vol => vol.Chapters)
|
||||
.ThenInclude(c => c.Files)
|
||||
.AsSplitQuery()
|
||||
.OrderBy(vol => vol.Number)
|
||||
.OrderBy(vol => vol.MinNumber)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ public class VolumeRepository : IVolumeRepository
|
||||
.ThenInclude(c => c.People)
|
||||
.Include(vol => vol.Chapters)
|
||||
.ThenInclude(c => c.Tags)
|
||||
.OrderBy(volume => volume.Number)
|
||||
.OrderBy(volume => volume.MinNumber)
|
||||
.ProjectTo<VolumeDto>(_mapper.ConfigurationProvider)
|
||||
.AsNoTracking()
|
||||
.AsSplitQuery()
|
||||
@ -215,7 +215,7 @@ public class VolumeRepository : IVolumeRepository
|
||||
|
||||
private static void SortSpecialChapters(IEnumerable<VolumeDto> volumes)
|
||||
{
|
||||
foreach (var v in volumes.Where(vDto => vDto.Number == 0))
|
||||
foreach (var v in volumes.Where(vDto => vDto.MinNumber == 0))
|
||||
{
|
||||
v.Chapters = v.Chapters.OrderByNatural(x => x.Range).ToList();
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ public class ScrobbleEvent : IEntityDate
|
||||
/// <summary>
|
||||
/// Depends on the ScrobbleEvent if filled in
|
||||
/// </summary>
|
||||
public int? VolumeNumber { get; set; }
|
||||
public int? VolumeNumber { get; set; } // TODO: Migrate this to float
|
||||
/// <summary>
|
||||
/// Has this event been processed and pushed to Provider
|
||||
/// </summary>
|
||||
|
@ -15,7 +15,17 @@ public class Volume : IEntityDate, IHasReadTimeEstimate
|
||||
/// <summary>
|
||||
/// The minimum number in the Name field in Int form
|
||||
/// </summary>
|
||||
public required int Number { get; set; }
|
||||
/// <remarks>Removed in v0.7.13.8, this was an int and we need the ability to have 0.5 volumes render on the UI</remarks>
|
||||
[Obsolete("Use MinNumber and MaxNumber instead")]
|
||||
public int Number { get; set; }
|
||||
/// <summary>
|
||||
/// The minimum number in the Name field
|
||||
/// </summary>
|
||||
public required float MinNumber { get; set; }
|
||||
/// <summary>
|
||||
/// The maximum number in the Name field (same as Minimum if Name isn't a range)
|
||||
/// </summary>
|
||||
public required float MaxNumber { get; set; }
|
||||
public IList<Chapter> Chapters { get; set; } = null!;
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime LastModified { get; set; }
|
||||
|
@ -18,8 +18,8 @@ public static class SeriesExtensions
|
||||
/// <remarks>This is under the assumption that the Volume already has a Cover Image calculated and set</remarks>
|
||||
public static string? GetCoverImage(this Series series)
|
||||
{
|
||||
var volumes = (series.Volumes ?? new List<Volume>())
|
||||
.OrderBy(v => v.Number, ChapterSortComparer.Default)
|
||||
var volumes = (series.Volumes ?? [])
|
||||
.OrderBy(v => v.MinNumber, ChapterSortComparer.Default)
|
||||
.ToList();
|
||||
var firstVolume = volumes.GetCoverImage(series.Format);
|
||||
if (firstVolume == null) return null;
|
||||
@ -34,20 +34,20 @@ public static class SeriesExtensions
|
||||
}
|
||||
|
||||
// just volumes
|
||||
if (volumes.TrueForAll(v => $"{v.Number}" != Parser.DefaultVolume))
|
||||
if (volumes.TrueForAll(v => $"{v.MinNumber}" != Parser.DefaultVolume))
|
||||
{
|
||||
return firstVolume.CoverImage;
|
||||
}
|
||||
// If we have loose leaf chapters
|
||||
|
||||
// if loose leaf chapters AND volumes, just return first volume
|
||||
if (volumes.Count >= 1 && $"{volumes[0].Number}" != Parser.DefaultVolume)
|
||||
if (volumes.Count >= 1 && $"{volumes[0].MinNumber}" != Parser.DefaultVolume)
|
||||
{
|
||||
var looseLeafChapters = volumes.Where(v => $"{v.Number}" == Parser.DefaultVolume)
|
||||
var looseLeafChapters = volumes.Where(v => $"{v.MinNumber}" == Parser.DefaultVolume)
|
||||
.SelectMany(c => c.Chapters.Where(c => !c.IsSpecial))
|
||||
.OrderBy(c => c.Number.AsDouble(), ChapterSortComparerZeroFirst.Default)
|
||||
.ToList();
|
||||
if (looseLeafChapters.Count > 0 && (1.0f * volumes[0].Number) > looseLeafChapters[0].Number.AsFloat())
|
||||
if (looseLeafChapters.Count > 0 && (1.0f * volumes[0].MinNumber) > looseLeafChapters[0].Number.AsFloat())
|
||||
{
|
||||
return looseLeafChapters[0].CoverImage;
|
||||
}
|
||||
@ -55,7 +55,7 @@ public static class SeriesExtensions
|
||||
}
|
||||
|
||||
var firstLooseLeafChapter = volumes
|
||||
.Where(v => $"{v.Number}" == Parser.DefaultVolume)
|
||||
.Where(v => $"{v.MinNumber}" == Parser.DefaultVolume)
|
||||
.SelectMany(v => v.Chapters)
|
||||
.OrderBy(c => c.Number.AsDouble(), ChapterSortComparerZeroFirst.Default)
|
||||
.FirstOrDefault(c => !c.IsSpecial);
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
|
||||
namespace API.Extensions;
|
||||
#nullable enable
|
||||
@ -22,16 +23,16 @@ public static class VolumeListExtensions
|
||||
|
||||
if (seriesFormat == MangaFormat.Epub || seriesFormat == MangaFormat.Pdf)
|
||||
{
|
||||
return volumes.MinBy(x => x.Number);
|
||||
return volumes.MinBy(x => x.MinNumber);
|
||||
}
|
||||
|
||||
|
||||
if (volumes.Any(x => x.Number != 0))
|
||||
if (volumes.Any(x => x.MinNumber != 0f)) // TODO: Refactor this so we can avoid a magic number
|
||||
{
|
||||
return volumes.OrderBy(x => x.Number).FirstOrDefault(x => x.Number != 0);
|
||||
return volumes.OrderBy(x => x.MinNumber).FirstOrDefault(x => x.MinNumber != 0);
|
||||
}
|
||||
|
||||
// We only have 1 volume of chapters, we need to be cautious if there are specials, as we don't want to order them first
|
||||
return volumes.MinBy(x => x.Number);
|
||||
return volumes.MinBy(x => x.MinNumber);
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ public class VolumeBuilder : IEntityBuilder<Volume>
|
||||
_volume = new Volume()
|
||||
{
|
||||
Name = volumeNumber,
|
||||
// TODO / BUG: Try to use float based Number which will allow Epub's with < 1 volumes to show in series detail
|
||||
Number = (int) Services.Tasks.Scanner.Parser.Parser.MinNumberFromRange(volumeNumber),
|
||||
MinNumber = Services.Tasks.Scanner.Parser.Parser.MinNumberFromRange(volumeNumber),
|
||||
MaxNumber = Services.Tasks.Scanner.Parser.Parser.MaxNumberFromRange(volumeNumber),
|
||||
Chapters = new List<Chapter>()
|
||||
};
|
||||
}
|
||||
@ -27,9 +27,25 @@ public class VolumeBuilder : IEntityBuilder<Volume>
|
||||
return this;
|
||||
}
|
||||
|
||||
public VolumeBuilder WithNumber(int number)
|
||||
public VolumeBuilder WithNumber(float number)
|
||||
{
|
||||
_volume.Number = number;
|
||||
_volume.MinNumber = number;
|
||||
if (_volume.MaxNumber < number)
|
||||
{
|
||||
_volume.MaxNumber = number;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public VolumeBuilder WithMinNumber(float number)
|
||||
{
|
||||
_volume.MinNumber = number;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VolumeBuilder WithMaxNumber(float number)
|
||||
{
|
||||
_volume.MaxNumber = number;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -298,7 +298,7 @@ public class ScrobblingService : IScrobblingService
|
||||
var prevVol = $"{existingEvt.VolumeNumber}";
|
||||
|
||||
existingEvt.VolumeNumber =
|
||||
await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadVolumeForSeries(seriesId, userId);
|
||||
(int) await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadVolumeForSeries(seriesId, userId);
|
||||
existingEvt.ChapterNumber =
|
||||
await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadChapterForSeries(seriesId, userId);
|
||||
_unitOfWork.ScrobbleRepository.Update(existingEvt);
|
||||
@ -319,7 +319,7 @@ public class ScrobblingService : IScrobblingService
|
||||
MalId = ExtractId<long?>(series.Metadata.WebLinks, MalWeblinkWebsite),
|
||||
AppUserId = userId,
|
||||
VolumeNumber =
|
||||
await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadVolumeForSeries(seriesId, userId),
|
||||
(int) await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadVolumeForSeries(seriesId, userId),
|
||||
ChapterNumber =
|
||||
await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadChapterForSeries(seriesId, userId),
|
||||
Format = LibraryTypeHelper.GetFormat(series.Library.Type),
|
||||
@ -660,7 +660,7 @@ public class ScrobblingService : IScrobblingService
|
||||
foreach (var readEvt in readEvents)
|
||||
{
|
||||
readEvt.VolumeNumber =
|
||||
await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadVolumeForSeries(readEvt.SeriesId,
|
||||
(int) await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadVolumeForSeries(readEvt.SeriesId,
|
||||
readEvt.AppUser.Id);
|
||||
readEvt.ChapterNumber =
|
||||
await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadChapterForSeries(readEvt.SeriesId,
|
||||
|
@ -365,18 +365,17 @@ public class ReaderService : IReaderService
|
||||
var currentVolume = volumes.Single(v => v.Id == volumeId);
|
||||
var currentChapter = currentVolume.Chapters.Single(c => c.Id == currentChapterId);
|
||||
|
||||
if (currentVolume.Number == 0)
|
||||
if (currentVolume.MinNumber == 0)
|
||||
{
|
||||
// Handle specials by sorting on their Filename aka Range
|
||||
var chapterId = GetNextChapterId(currentVolume.Chapters.OrderByNatural(x => x.Range), currentChapter.Range, dto => dto.Range);
|
||||
if (chapterId > 0) return chapterId;
|
||||
}
|
||||
|
||||
var currentVolumeNumber = currentVolume.Name.AsFloat();
|
||||
var next = false;
|
||||
foreach (var volume in volumes)
|
||||
{
|
||||
var volumeNumbersMatch = Math.Abs(volume.Name.AsFloat() - currentVolumeNumber) < 0.00001f;
|
||||
var volumeNumbersMatch = volume.Name == currentVolume.Name;
|
||||
if (volumeNumbersMatch && volume.Chapters.Count > 1)
|
||||
{
|
||||
// Handle Chapters within current Volume
|
||||
@ -420,9 +419,9 @@ public class ReaderService : IReaderService
|
||||
else if (firstChapter.Number.AsDouble() == 0) return firstChapter.Id;
|
||||
|
||||
// If on last volume AND there are no specials left, then let's return -1
|
||||
var anySpecials = volumes.Where(v => $"{v.Number}" == Parser.DefaultVolume)
|
||||
var anySpecials = volumes.Where(v => $"{v.MinNumber}" == Parser.DefaultVolume)
|
||||
.SelectMany(v => v.Chapters.Where(c => c.IsSpecial)).Any();
|
||||
if (currentVolume.Number != 0 && !anySpecials)
|
||||
if (currentVolume.MinNumber != 0 && !anySpecials)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@ -434,10 +433,10 @@ public class ReaderService : IReaderService
|
||||
// This has an added problem that it will loop up to the beginning always
|
||||
// Should I change this to Max number? volumes.LastOrDefault()?.Number -> volumes.Max(v => v.Number)
|
||||
|
||||
if (currentVolume.Number != 0 && currentVolume.Number == volumes.LastOrDefault()?.Number && volumes.Count > 1)
|
||||
if (currentVolume.MinNumber != 0 && currentVolume.MinNumber == volumes.LastOrDefault()?.MinNumber && volumes.Count > 1)
|
||||
{
|
||||
var chapterVolume = volumes.FirstOrDefault();
|
||||
if (chapterVolume?.Number != 0) return -1;
|
||||
if (chapterVolume?.MinNumber != 0) return -1;
|
||||
|
||||
// This is my attempt at fixing a bug where we loop around to the beginning, but I just can't seem to figure it out
|
||||
// var orderedVolumes = volumes.OrderBy(v => v.Number, SortComparerZeroLast.Default).ToList();
|
||||
@ -479,7 +478,7 @@ public class ReaderService : IReaderService
|
||||
var currentVolume = volumes.Single(v => v.Id == volumeId);
|
||||
var currentChapter = currentVolume.Chapters.Single(c => c.Id == currentChapterId);
|
||||
|
||||
if (currentVolume.Number == 0)
|
||||
if (currentVolume.MinNumber == 0)
|
||||
{
|
||||
var chapterId = GetNextChapterId(currentVolume.Chapters.OrderByNatural(x => x.Range).Reverse(), currentChapter.Range,
|
||||
dto => dto.Range);
|
||||
@ -489,7 +488,7 @@ public class ReaderService : IReaderService
|
||||
var next = false;
|
||||
foreach (var volume in volumes)
|
||||
{
|
||||
if (volume.Number == currentVolume.Number)
|
||||
if (volume.MinNumber == currentVolume.MinNumber)
|
||||
{
|
||||
var chapterId = GetNextChapterId(currentVolume.Chapters.OrderBy(x => x.Number.AsDouble(), _chapterSortComparerForInChapterSorting).Reverse(),
|
||||
currentChapter.Range, dto => dto.Range);
|
||||
@ -499,15 +498,15 @@ public class ReaderService : IReaderService
|
||||
}
|
||||
if (next)
|
||||
{
|
||||
if (currentVolume.Number - 1 == 0) break; // If we have walked all the way to chapter volume, then we should break so logic outside can work
|
||||
if (currentVolume.MinNumber - 1 == 0) break; // If we have walked all the way to chapter volume, then we should break so logic outside can work
|
||||
var lastChapter = volume.Chapters.MaxBy(x => x.Number.AsDouble(), _chapterSortComparerForInChapterSorting);
|
||||
if (lastChapter == null) return -1;
|
||||
return lastChapter.Id;
|
||||
}
|
||||
}
|
||||
|
||||
var lastVolume = volumes.MaxBy(v => v.Number);
|
||||
if (currentVolume.Number == 0 && currentVolume.Number != lastVolume?.Number && lastVolume?.Chapters.Count > 1)
|
||||
var lastVolume = volumes.MaxBy(v => v.MinNumber);
|
||||
if (currentVolume.MinNumber == 0 && currentVolume.MinNumber != lastVolume?.MinNumber && lastVolume?.Chapters.Count > 1)
|
||||
{
|
||||
var lastChapter = lastVolume.Chapters.MaxBy(x => x.Number.AsDouble(), _chapterSortComparerForInChapterSorting);
|
||||
if (lastChapter == null) return -1;
|
||||
@ -532,13 +531,13 @@ public class ReaderService : IReaderService
|
||||
if (!await _unitOfWork.AppUserProgressRepository.AnyUserProgressForSeriesAsync(seriesId, userId))
|
||||
{
|
||||
// I think i need a way to sort volumes last
|
||||
return volumes.OrderBy(v => v.Number.ToString(CultureInfo.InvariantCulture).AsDouble(), _chapterSortComparer).First().Chapters
|
||||
return volumes.OrderBy(v => v.MinNumber.ToString(CultureInfo.InvariantCulture).AsDouble(), _chapterSortComparer).First().Chapters
|
||||
.OrderBy(c => c.Number.AsFloat()).First();
|
||||
}
|
||||
|
||||
// Loop through all chapters that are not in volume 0
|
||||
var volumeChapters = volumes
|
||||
.Where(v => v.Number != 0)
|
||||
.Where(v => v.MinNumber != 0)
|
||||
.SelectMany(v => v.Chapters)
|
||||
.ToList();
|
||||
|
||||
@ -550,7 +549,7 @@ public class ReaderService : IReaderService
|
||||
if (currentlyReadingChapter != null) return currentlyReadingChapter;
|
||||
|
||||
// Order with volume 0 last so we prefer the natural order
|
||||
return FindNextReadingChapter(volumes.OrderBy(v => v.Number, SortComparerZeroLast.Default)
|
||||
return FindNextReadingChapter(volumes.OrderBy(v => v.MinNumber, SortComparerZeroLast.Default)
|
||||
.SelectMany(v => v.Chapters.OrderBy(c => c.Number.AsDouble()))
|
||||
.ToList());
|
||||
}
|
||||
@ -619,7 +618,7 @@ public class ReaderService : IReaderService
|
||||
public async Task MarkChaptersUntilAsRead(AppUser user, int seriesId, float chapterNumber)
|
||||
{
|
||||
var volumes = await _unitOfWork.VolumeRepository.GetVolumesForSeriesAsync(new List<int> { seriesId }, true);
|
||||
foreach (var volume in volumes.OrderBy(v => v.Number))
|
||||
foreach (var volume in volumes.OrderBy(v => v.MinNumber))
|
||||
{
|
||||
var chapters = volume.Chapters
|
||||
.Where(c => !c.IsSpecial && Parser.MaxNumberFromRange(c.Range) <= chapterNumber)
|
||||
@ -631,7 +630,7 @@ public class ReaderService : IReaderService
|
||||
public async Task MarkVolumesUntilAsRead(AppUser user, int seriesId, int volumeNumber)
|
||||
{
|
||||
var volumes = await _unitOfWork.VolumeRepository.GetVolumesForSeriesAsync(new List<int> { seriesId }, true);
|
||||
foreach (var volume in volumes.Where(v => v.Number <= volumeNumber && v.Number > 0).OrderBy(v => v.Number))
|
||||
foreach (var volume in volumes.Where(v => v.MinNumber <= volumeNumber && v.MinNumber > 0).OrderBy(v => v.MinNumber))
|
||||
{
|
||||
await MarkChaptersAsRead(user, volume.SeriesId, volume.Chapters);
|
||||
}
|
||||
|
@ -633,7 +633,7 @@ public class ReadingListService : IReadingListService
|
||||
var bookVolume = string.IsNullOrEmpty(book.Volume)
|
||||
? Parser.DefaultVolume
|
||||
: book.Volume;
|
||||
var matchingVolume = bookSeries.Volumes.Find(v => bookVolume == v.Name) ?? bookSeries.Volumes.Find(v => v.Number == 0);
|
||||
var matchingVolume = bookSeries.Volumes.Find(v => bookVolume == v.Name) ?? bookSeries.Volumes.Find(v => v.MinNumber == 0);
|
||||
if (matchingVolume == null)
|
||||
{
|
||||
importSummary.Results.Add(new CblBookResult(book)
|
||||
|
@ -99,7 +99,7 @@ public class SeriesService : ISeriesService
|
||||
.FirstOrDefault();
|
||||
|
||||
if (minVolumeNumber != null && minChapter != null && float.TryParse(minChapter.Number, CultureInfo.InvariantCulture, out var chapNum) &&
|
||||
(chapNum >= minVolumeNumber.Number || chapNum == 0))
|
||||
(chapNum >= minVolumeNumber.MinNumber || chapNum == 0))
|
||||
{
|
||||
return minVolumeNumber.Chapters.MinBy(c => c.Number.AsFloat(), ChapterSortComparer.Default);
|
||||
}
|
||||
@ -521,7 +521,7 @@ public class SeriesService : ISeriesService
|
||||
}
|
||||
else
|
||||
{
|
||||
processedVolumes = volumes.Where(v => v.Number > 0).ToList();
|
||||
processedVolumes = volumes.Where(v => v.MinNumber > 0).ToList();
|
||||
processedVolumes.ForEach(v =>
|
||||
{
|
||||
v.Name = $"Volume {v.Name}";
|
||||
@ -532,7 +532,7 @@ public class SeriesService : ISeriesService
|
||||
var specials = new List<ChapterDto>();
|
||||
var chapters = volumes.SelectMany(v => v.Chapters.Select(c =>
|
||||
{
|
||||
if (v.Number == 0) return c;
|
||||
if (v.MinNumber == 0) return c;
|
||||
c.VolumeTitle = v.Name;
|
||||
return c;
|
||||
}).OrderBy(c => c.Number.AsFloat(), ChapterSortComparer.Default)).ToList();
|
||||
@ -558,7 +558,7 @@ public class SeriesService : ISeriesService
|
||||
}
|
||||
|
||||
var storylineChapters = volumes
|
||||
.Where(v => v.Number == 0)
|
||||
.Where(v => v.MinNumber == 0)
|
||||
.SelectMany(v => v.Chapters.Where(c => !c.IsSpecial))
|
||||
.OrderBy(c => c.Number.AsFloat(), ChapterSortComparer.Default)
|
||||
.ToList();
|
||||
@ -799,7 +799,7 @@ public class SeriesService : ISeriesService
|
||||
float.TryParse(lastChapter.Number, NumberStyles.Number, CultureInfo.InvariantCulture,
|
||||
out var lastChapterNumber);
|
||||
|
||||
var lastVolumeNum = chapters.Select(c => c.Volume.Number).Max();
|
||||
var lastVolumeNum = chapters.Select(c => c.Volume.MinNumber).Max();
|
||||
|
||||
var result = new NextExpectedChapterDto
|
||||
{
|
||||
@ -812,7 +812,7 @@ public class SeriesService : ISeriesService
|
||||
if (lastChapterNumber > 0)
|
||||
{
|
||||
result.ChapterNumber = (int) Math.Truncate(lastChapterNumber) + 1;
|
||||
result.VolumeNumber = lastChapter.Volume.Number;
|
||||
result.VolumeNumber = lastChapter.Volume.MinNumber;
|
||||
result.Title = series.Library.Type switch
|
||||
{
|
||||
LibraryType.Manga => await _localizationService.Translate(userId, "chapter-num", result.ChapterNumber),
|
||||
|
@ -287,7 +287,7 @@ public class StatisticService : IStatisticService
|
||||
TotalPeople = distinctPeople,
|
||||
TotalSize = await _context.MangaFile.SumAsync(m => m.Bytes),
|
||||
TotalTags = await _context.Tag.CountAsync(),
|
||||
VolumeCount = await _context.Volume.Where(v => v.Number != 0).CountAsync(),
|
||||
VolumeCount = await _context.Volume.Where(v => v.MinNumber != 0).CountAsync(),
|
||||
MostActiveUsers = mostActiveUsers,
|
||||
MostActiveLibraries = mostActiveLibrary,
|
||||
MostPopularSeries = mostPopularSeries,
|
||||
|
@ -9,6 +9,7 @@ using System.Linq;
|
||||
using API.Comparators;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using AutoMapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@ -68,21 +69,21 @@ public class TachiyomiService : ITachiyomiService
|
||||
|
||||
// Else return the max chapter to Tachiyomi so it can consider everything read
|
||||
var volumes = (await _unitOfWork.VolumeRepository.GetVolumes(seriesId)).ToImmutableList();
|
||||
var looseLeafChapterVolume = volumes.Find(v => v.Number == 0);
|
||||
var looseLeafChapterVolume = volumes.Find(v => v.MinNumber == 0);
|
||||
if (looseLeafChapterVolume == null)
|
||||
{
|
||||
var volumeChapter = _mapper.Map<ChapterDto>(volumes
|
||||
[^1].Chapters
|
||||
.OrderBy(c => c.Number.AsFloat(), ChapterSortComparerZeroFirst.Default)
|
||||
.Last());
|
||||
if (volumeChapter.Number == "0")
|
||||
if (volumeChapter.Number == Parser.DefaultVolume)
|
||||
{
|
||||
var volume = volumes.First(v => v.Id == volumeChapter.VolumeId);
|
||||
return new ChapterDto()
|
||||
{
|
||||
// Use R to ensure that localization of underlying system doesn't affect the stringification
|
||||
// https://docs.microsoft.com/en-us/globalization/locale/number-formatting-in-dotnet-framework
|
||||
Number = (volume.Number / 10_000f).ToString("R", EnglishCulture)
|
||||
Number = (volume.MinNumber / 10_000f).ToString("R", EnglishCulture)
|
||||
};
|
||||
}
|
||||
|
||||
@ -103,14 +104,14 @@ public class TachiyomiService : ITachiyomiService
|
||||
|
||||
var volumeWithProgress = await _unitOfWork.VolumeRepository.GetVolumeDtoAsync(prevChapter.VolumeId, userId);
|
||||
// We only encode for single-file volumes
|
||||
if (volumeWithProgress!.Number != 0 && volumeWithProgress.Chapters.Count == 1)
|
||||
if (volumeWithProgress!.MinNumber != 0 && volumeWithProgress.Chapters.Count == 1)
|
||||
{
|
||||
// The progress is on a volume, encode it as a fake chapterDTO
|
||||
return new ChapterDto()
|
||||
{
|
||||
// Use R to ensure that localization of underlying system doesn't affect the stringification
|
||||
// https://docs.microsoft.com/en-us/globalization/locale/number-formatting-in-dotnet-framework
|
||||
Number = (volumeWithProgress.Number / 10_000f).ToString("R", EnglishCulture)
|
||||
Number = (volumeWithProgress.MinNumber / 10_000f).ToString("R", EnglishCulture)
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ public class StatsService : IStatsService
|
||||
.AsNoTracking()
|
||||
.AsSplitQuery()
|
||||
.MaxAsync(s => s.Volumes!
|
||||
.Where(v => v.Number == 0)
|
||||
.Where(v => v.MinNumber == 0)
|
||||
.SelectMany(v => v.Chapters!)
|
||||
.Count());
|
||||
}
|
||||
|
@ -244,8 +244,10 @@ public class Startup
|
||||
await MigrateSmartFilterEncoding.Migrate(unitOfWork, dataContext, logger);
|
||||
await MigrateLibrariesToHaveAllFileTypes.Migrate(unitOfWork, dataContext, logger);
|
||||
|
||||
|
||||
// v0.7.14
|
||||
await MigrateEmailTemplates.Migrate(directoryService, logger);
|
||||
await MigrateVolumeNumber.Migrate(unitOfWork, dataContext, logger);
|
||||
|
||||
// Update the version in the DB after all migrations are run
|
||||
var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion);
|
||||
|
@ -10,12 +10,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Cronos" Version="0.8.1" />
|
||||
<PackageReference Include="Cronos" Version="0.8.2" />
|
||||
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.17.0.82934">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.18.0.83559">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -7,7 +7,7 @@ import { AgeRating } from './metadata/age-rating';
|
||||
export interface Chapter {
|
||||
id: number;
|
||||
range: string;
|
||||
number: string;
|
||||
minNumber: string;
|
||||
files: Array<MangaFile>;
|
||||
/**
|
||||
* This is used in the UI, it is not updated or sent to Backend
|
||||
|
@ -3,7 +3,8 @@ import { HourEstimateRange } from './series-detail/hour-estimate-range';
|
||||
|
||||
export interface Volume {
|
||||
id: number;
|
||||
number: number;
|
||||
minNumber: number;
|
||||
maxNumber: number;
|
||||
name: string;
|
||||
createdUtc: string;
|
||||
lastModifiedUtc: string;
|
||||
|
@ -282,7 +282,7 @@ export class EditSeriesModalComponent implements OnInit {
|
||||
});
|
||||
this.seriesVolumes.forEach(vol => {
|
||||
vol.volumeFiles = vol.chapters?.sort(this.utilityService.sortChapters).map((c: Chapter) => c.files.map((f: any) => {
|
||||
f.chapter = c.number;
|
||||
f.chapter = c.minNumber;
|
||||
return f;
|
||||
})).flat();
|
||||
});
|
||||
|
@ -123,7 +123,7 @@
|
||||
<span>
|
||||
<app-card-actionables (actionHandler)="performAction($event, chapter)" [actions]="chapterActions"
|
||||
[labelBy]="utilityService.formatChapterName(libraryType, true, true) + formatChapterNumber(chapter)"></app-card-actionables>
|
||||
<ng-container *ngIf="chapter.number !== '0'; else specialHeader">
|
||||
<ng-container *ngIf="chapter.minNumber !== '0'; else specialHeader">
|
||||
{{utilityService.formatChapterName(libraryType, true, false) }} {{formatChapterNumber(chapter)}}
|
||||
</ng-container>
|
||||
</span>
|
||||
|
@ -182,10 +182,10 @@ export class CardDetailDrawerComponent implements OnInit {
|
||||
}
|
||||
|
||||
formatChapterNumber(chapter: Chapter) {
|
||||
if (chapter.number === '0') {
|
||||
if (chapter.minNumber === '0') {
|
||||
return '1';
|
||||
}
|
||||
return chapter.number;
|
||||
return chapter.minNumber;
|
||||
}
|
||||
|
||||
performAction(action: ActionItem<any>, chapter: Chapter) {
|
||||
|
@ -7,9 +7,9 @@
|
||||
<ng-template #fullComicTitle>
|
||||
{{seriesName.length > 0 ? seriesName + ' - ' : ''}}
|
||||
<ng-container *ngIf="includeVolume && volumeTitle !== ''">
|
||||
{{entity.number !== 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
{{entity.minNumber !== 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
</ng-container>
|
||||
{{entity.number !== 0 ? (isChapter ? t('issue-num') + entity.number : volumeTitle) : t('special')}}
|
||||
{{entity.minNumber !== 0 ? (isChapter ? t('issue-num') + entity.minNumber : volumeTitle) : t('special')}}
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="LibraryType.Manga">
|
||||
@ -19,9 +19,9 @@
|
||||
<ng-template #fullMangaTitle>
|
||||
{{seriesName.length > 0 ? seriesName + ' - ' : ''}}
|
||||
<ng-container *ngIf="includeVolume && volumeTitle !== ''">
|
||||
{{entity.number !== 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
{{entity.minNumber !== 0 ? (isChapter && includeVolume ? volumeTitle : '') : ''}}
|
||||
</ng-container>
|
||||
{{entity.number !== 0 ? (isChapter ? (t('chapter') + ' ') + entity.number : volumeTitle) : t('special')}}
|
||||
{{entity.minNumber !== 0 ? (isChapter ? (t('chapter') + ' ') + entity.minNumber : volumeTitle) : t('special')}}
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="LibraryType.Book">
|
||||
|
@ -226,7 +226,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
||||
/**
|
||||
* Track by function for Chapter to tell when to refresh card data
|
||||
*/
|
||||
trackByChapterIdentity = (index: number, item: Chapter) => `${item.title}_${item.number}_${item.volumeId}_${item.pagesRead}`;
|
||||
trackByChapterIdentity = (index: number, item: Chapter) => `${item.title}_${item.minNumber}_${item.volumeId}_${item.pagesRead}`;
|
||||
trackByRelatedSeriesIdentify = (index: number, item: RelatedSeriesPair) => `${item.series.name}_${item.series.libraryId}_${item.series.pagesRead}_${item.relation}`;
|
||||
trackBySeriesIdentify = (index: number, item: Series) => `${item.name}_${item.libraryId}_${item.pagesRead}`;
|
||||
trackByStoryLineIdentity = (index: number, item: StoryLineItem) => {
|
||||
@ -341,13 +341,13 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
||||
|
||||
// This is a lone chapter
|
||||
if (vol.length === 0) {
|
||||
return 'Ch ' + this.currentlyReadingChapter.number;
|
||||
return 'Ch ' + this.currentlyReadingChapter.minNumber;
|
||||
}
|
||||
|
||||
if (this.currentlyReadingChapter.number === "0") {
|
||||
return 'Vol ' + vol[0].number;
|
||||
if (this.currentlyReadingChapter.minNumber === "0") {
|
||||
return 'Vol ' + vol[0].minNumber;
|
||||
}
|
||||
return 'Vol ' + vol[0].number + ' Ch ' + this.currentlyReadingChapter.number;
|
||||
return 'Vol ' + vol[0].minNumber + ' Ch ' + this.currentlyReadingChapter.minNumber;
|
||||
}
|
||||
|
||||
return this.currentlyReadingChapter.title;
|
||||
|
@ -113,9 +113,9 @@ export class DownloadService {
|
||||
case 'series':
|
||||
return (downloadEntity as Series).name;
|
||||
case 'volume':
|
||||
return (downloadEntity as Volume).number + '';
|
||||
return (downloadEntity as Volume).minNumber + '';
|
||||
case 'chapter':
|
||||
return (downloadEntity as Chapter).number;
|
||||
return (downloadEntity as Chapter).minNumber;
|
||||
case 'bookmark':
|
||||
return '';
|
||||
case 'logs':
|
||||
|
@ -43,7 +43,7 @@ export class UtilityService {
|
||||
|
||||
|
||||
sortChapters = (a: Chapter, b: Chapter) => {
|
||||
return parseFloat(a.number) - parseFloat(b.number);
|
||||
return parseFloat(a.minNumber) - parseFloat(b.minNumber);
|
||||
}
|
||||
|
||||
mangaFormatToText(format: MangaFormat): string {
|
||||
|
29
openapi.json
29
openapi.json
@ -7,7 +7,7 @@
|
||||
"name": "GPL-3.0",
|
||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||
},
|
||||
"version": "0.7.13.6"
|
||||
"version": "0.7.13.7"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
@ -16338,8 +16338,8 @@
|
||||
"format": "float"
|
||||
},
|
||||
"volumeNumber": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"expectedDate": {
|
||||
"type": "string",
|
||||
@ -20245,7 +20245,18 @@
|
||||
"number": {
|
||||
"type": "integer",
|
||||
"description": "The minimum number in the Name field in Int form",
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"deprecated": true
|
||||
},
|
||||
"minNumber": {
|
||||
"type": "number",
|
||||
"description": "The minimum number in the Name field",
|
||||
"format": "float"
|
||||
},
|
||||
"maxNumber": {
|
||||
"type": "number",
|
||||
"description": "The maximum number in the Name field (same as Minimum if Name isn't a range)",
|
||||
"format": "float"
|
||||
},
|
||||
"chapters": {
|
||||
"type": "array",
|
||||
@ -20314,9 +20325,13 @@
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"number": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
"minNumber": {
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"maxNumber": {
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
|
Loading…
x
Reference in New Issue
Block a user