Float-based Volumes (#2659)

This commit is contained in:
Joe Milazzo 2024-01-28 11:37:38 -06:00 committed by GitHub
parent 6fdc9228df
commit f6af6d66be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 3106 additions and 184 deletions

View file

@ -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,

View file

@ -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);
}

View file

@ -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)

View file

@ -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),

View file

@ -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,

View file

@ -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)
};
}

View file

@ -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());
}