Lots of Bugfixes (#2977)

This commit is contained in:
Joe Milazzo 2024-06-04 17:43:15 -05:00 committed by GitHub
parent 8c629695ef
commit 616ed7a75d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 427 additions and 244 deletions

View file

@ -847,7 +847,7 @@ public class BookService : IBookService
Filename = Path.GetFileName(filePath),
Title = specialName?.Trim() ?? string.Empty,
FullFilePath = Parser.NormalizePath(filePath),
IsSpecial = false,
IsSpecial = Parser.HasSpecialMarker(filePath),
Series = series.Trim(),
SeriesSort = series.Trim(),
Volumes = seriesIndex
@ -869,7 +869,7 @@ public class BookService : IBookService
Filename = Path.GetFileName(filePath),
Title = epubBook.Title.Trim(),
FullFilePath = Parser.NormalizePath(filePath),
IsSpecial = false,
IsSpecial = Parser.HasSpecialMarker(filePath),
Series = epubBook.Title.Trim(),
Volumes = Parser.LooseLeafVolume,
};

View file

@ -322,7 +322,7 @@ public class CacheService : ICacheService
var path = GetCachePath(chapterId);
// NOTE: We can optimize this by extracting and renaming, so we don't need to scan for the files and can do a direct access
var files = _directoryService.GetFilesWithExtension(path, Tasks.Scanner.Parser.Parser.ImageFileExtensions)
.OrderByNatural(Path.GetFileNameWithoutExtension)
//.OrderByNatural(Path.GetFileNameWithoutExtension) // This is already done in GetPageFromFiles
.ToArray();
return GetPageFromFiles(files, page);

View file

@ -228,7 +228,7 @@ public class ScrobblingService : IScrobblingService
LibraryId = series.LibraryId,
ScrobbleEventType = ScrobbleEventType.Review,
AniListId = ExtractId<int?>(series.Metadata.WebLinks, AniListWeblinkWebsite),
MalId = ExtractId<long?>(series.Metadata.WebLinks, MalWeblinkWebsite),
MalId = GetMalId(series),
AppUserId = userId,
Format = LibraryTypeHelper.GetFormat(series.Library.Type),
ReviewBody = reviewBody,
@ -250,7 +250,7 @@ public class ScrobblingService : IScrobblingService
{
if (!await _licenseService.HasActiveLicense()) return;
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata | SeriesIncludes.Library);
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata | SeriesIncludes.Library | SeriesIncludes.ExternalMetadata);
if (series == null) throw new KavitaException(await _localizationService.Translate(userId, "series-doesnt-exist"));
_logger.LogInformation("Processing Scrobbling rating event for {UserId} on {SeriesName}", userId, series.Name);
@ -274,22 +274,34 @@ public class ScrobblingService : IScrobblingService
SeriesId = series.Id,
LibraryId = series.LibraryId,
ScrobbleEventType = ScrobbleEventType.ScoreUpdated,
AniListId = ExtractId<int?>(series.Metadata.WebLinks, AniListWeblinkWebsite), // TODO: We can get this also from ExternalSeriesMetadata
MalId = ExtractId<long?>(series.Metadata.WebLinks, MalWeblinkWebsite),
AniListId = GetAniListId(series),
MalId = GetMalId(series),
AppUserId = userId,
Format = LibraryTypeHelper.GetFormat(series.Library.Type),
Rating = rating
};
_unitOfWork.ScrobbleRepository.Attach(evt);
await _unitOfWork.CommitAsync();
_logger.LogDebug("Added Scrobbling Rating update on {SeriesName} with Userid {UserId} ", series.Name, userId);
_logger.LogDebug("Added Scrobbling Rating update on {SeriesName} with Userid {UserId}", series.Name, userId);
}
private static long? GetMalId(Series series)
{
var malId = ExtractId<long?>(series.Metadata.WebLinks, MalWeblinkWebsite);
return malId ?? series.ExternalSeriesMetadata.MalId;
}
private static int? GetAniListId(Series series)
{
var aniListId = ExtractId<int?>(series.Metadata.WebLinks, AniListWeblinkWebsite);
return aniListId ?? series.ExternalSeriesMetadata.AniListId;
}
public async Task ScrobbleReadingUpdate(int userId, int seriesId)
{
if (!await _licenseService.HasActiveLicense()) return;
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata | SeriesIncludes.Library);
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata | SeriesIncludes.Library | SeriesIncludes.ExternalMetadata);
if (series == null) throw new KavitaException(await _localizationService.Translate(userId, "series-doesnt-exist"));
_logger.LogInformation("Processing Scrobbling reading event for {UserId} on {SeriesName}", userId, series.Name);
@ -321,8 +333,8 @@ public class ScrobblingService : IScrobblingService
SeriesId = series.Id,
LibraryId = series.LibraryId,
ScrobbleEventType = ScrobbleEventType.ChapterRead,
AniListId = ExtractId<int?>(series.Metadata.WebLinks, AniListWeblinkWebsite),
MalId = ExtractId<long?>(series.Metadata.WebLinks, MalWeblinkWebsite),
AniListId = GetAniListId(series),
MalId = GetMalId(series),
AppUserId = userId,
VolumeNumber =
(int) await _unitOfWork.AppUserProgressRepository.GetHighestFullyReadVolumeForSeries(seriesId, userId),
@ -345,7 +357,7 @@ public class ScrobblingService : IScrobblingService
{
if (!await _licenseService.HasActiveLicense()) return;
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata | SeriesIncludes.Library);
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata | SeriesIncludes.Library | SeriesIncludes.ExternalMetadata);
if (series == null) throw new KavitaException(await _localizationService.Translate(userId, "series-doesnt-exist"));
_logger.LogInformation("Processing Scrobbling want-to-read event for {UserId} on {SeriesName}", userId, series.Name);
@ -360,8 +372,8 @@ public class ScrobblingService : IScrobblingService
SeriesId = series.Id,
LibraryId = series.LibraryId,
ScrobbleEventType = onWantToRead ? ScrobbleEventType.AddWantToRead : ScrobbleEventType.RemoveWantToRead,
AniListId = ExtractId<int?>(series.Metadata.WebLinks, AniListWeblinkWebsite),
MalId = ExtractId<long?>(series.Metadata.WebLinks, MalWeblinkWebsite),
AniListId = GetAniListId(series),
MalId = GetMalId(series),
AppUserId = userId,
Format = LibraryTypeHelper.GetFormat(series.Library.Type),
};

View file

@ -394,7 +394,7 @@ public class ReadingListService : IReadingListService
var existingChapterExists = readingList.Items.Select(rli => rli.ChapterId).ToHashSet();
var chaptersForSeries = (await _unitOfWork.ChapterRepository.GetChaptersByIdsAsync(chapterIds, ChapterIncludes.Volumes))
.OrderBy(c => c.Volume.MinNumber)
.ThenBy(x => x.MinNumber, _chapterSortComparerForInChapterSorting)
.ThenBy(x => x.SortOrder)
.ToList();
var index = readingList.Items.Count == 0 ? 0 : lastOrder + 1;

View file

@ -79,6 +79,7 @@ public class ScannedSeriesResult
public class SeriesModified
{
public required string? FolderPath { get; set; }
public required string? LowestFolderPath { get; set; }
public required string SeriesName { get; set; }
public DateTime LastScanned { get; set; }
public MangaFormat Format { get; set; }
@ -151,16 +152,28 @@ public class ParseScannedFiles
HasChanged = false
});
}
else if (seriesPaths.TryGetValue(normalizedPath, out var series) && series.All(s => !string.IsNullOrEmpty(s.LowestFolderPath)))
{
// If there are multiple series inside this path, let's check each of them to see which was modified and only scan those
// This is very helpful for ComicVine libraries by Publisher
foreach (var seriesModified in series)
{
if (HasSeriesFolderNotChangedSinceLastScan(seriesModified, seriesModified.LowestFolderPath!))
{
result.Add(CreateScanResult(directory, folderPath, false, ArraySegment<string>.Empty));
}
else
{
result.Add(CreateScanResult(directory, folderPath, true,
_directoryService.ScanFiles(seriesModified.LowestFolderPath!, fileExtensions, matcher)));
}
}
}
else
{
// For a scan, this is doing everything in the directory loop before the folder Action is called...which leads to no progress indication
result.Add(new ScanResult()
{
Files = _directoryService.ScanFiles(directory, fileExtensions, matcher),
Folder = directory,
LibraryRoot = folderPath,
HasChanged = true
});
result.Add(CreateScanResult(directory, folderPath, true,
_directoryService.ScanFiles(directory, fileExtensions)));
}
}
@ -175,26 +188,30 @@ public class ParseScannedFiles
if (HasSeriesFolderNotChangedSinceLastScan(seriesPaths, normalizedPath, forceCheck))
{
result.Add(new ScanResult()
{
Files = ArraySegment<string>.Empty,
Folder = folderPath,
LibraryRoot = libraryRoot,
HasChanged = false
});
result.Add(CreateScanResult(folderPath, libraryRoot, false, ArraySegment<string>.Empty));
}
else
{
result.Add(CreateScanResult(folderPath, libraryRoot, true,
_directoryService.ScanFiles(folderPath, fileExtensions)));
}
result.Add(new ScanResult()
{
Files = _directoryService.ScanFiles(folderPath, fileExtensions),
Folder = folderPath,
LibraryRoot = libraryRoot,
HasChanged = true
});
return result;
}
private static ScanResult CreateScanResult(string folderPath, string libraryRoot, bool hasChanged,
IList<string> files)
{
return new ScanResult()
{
Files = files,
Folder = folderPath,
LibraryRoot = libraryRoot,
HasChanged = hasChanged
};
}
/// <summary>
/// Attempts to either add a new instance of a series mapping to the _scannedSeries bag or adds to an existing.
@ -535,10 +552,29 @@ public class ParseScannedFiles
{
if (forceCheck) return false;
return seriesPaths.ContainsKey(normalizedFolder) && seriesPaths[normalizedFolder].All(f => f.LastScanned.Truncate(TimeSpan.TicksPerSecond) >=
_directoryService.GetLastWriteTime(normalizedFolder).Truncate(TimeSpan.TicksPerSecond));
if (seriesPaths.TryGetValue(normalizedFolder, out var v))
{
return HasAllSeriesFolderNotChangedSinceLastScan(v, normalizedFolder);
}
return false;
}
private bool HasAllSeriesFolderNotChangedSinceLastScan(IList<SeriesModified> seriesFolders,
string normalizedFolder)
{
return seriesFolders.All(f => HasSeriesFolderNotChangedSinceLastScan(f, normalizedFolder));
}
private bool HasSeriesFolderNotChangedSinceLastScan(SeriesModified seriesModified, string normalizedFolder)
{
return seriesModified.LastScanned.Truncate(TimeSpan.TicksPerSecond) >=
_directoryService.GetLastWriteTime(normalizedFolder)
.Truncate(TimeSpan.TicksPerSecond);
}
/// <summary>
/// Checks if there are any ParserInfos that have a Series that matches the LocalizedSeries field in any other info. If so,
/// rewrites the infos with series name instead of the localized name, so they stack.

View file

@ -12,8 +12,14 @@ public class BookParser(IDirectoryService directoryService, IBookService bookSer
info.ComicInfo = comicInfo;
// We need a special piece of code to override the Series IF there is a special marker in the filename for epub files
if (info.IsSpecial && info.Volumes == "0" && info.ComicInfo.Series != info.Series)
{
info.Series = info.ComicInfo.Series;
}
// This catches when original library type is Manga/Comic and when parsing with non
if (Parser.ParseVolume(info.Series, type) != Parser.LooseLeafVolume) // Shouldn't this be info.Volume != DefaultVolume?
if (Parser.ParseVolume(info.Series, type) != Parser.LooseLeafVolume)
{
var hasVolumeInTitle = !Parser.ParseVolume(info.Title, type)
.Equals(Parser.LooseLeafVolume);

View file

@ -103,7 +103,7 @@ public static class Parser
private static readonly Regex CoverImageRegex = new Regex(@"(?<![[a-z]\d])(?:!?)(?<!back)(?<!back_)(?<!back-)(cover|folder)(?![\w\d])",
MatchOptions, RegexTimeout);
private static readonly Regex NormalizeRegex = new Regex(@"[^\p{L}0-9\+!]",
private static readonly Regex NormalizeRegex = new Regex(@"[^\p{L}0-9\+!\*]",
MatchOptions, RegexTimeout);
/// <summary>

View file

@ -710,6 +710,12 @@ public class ProcessSeries : IProcessSeries
chapter.SortOrder = info.IssueOrder;
}
chapter.Range = chapter.GetNumberTitle();
if (float.TryParse(chapter.Title, out var _))
{
// If we have float based chapters, first scan can have the chapter formatted as Chapter 0.2 - .2 as the title is wrong.
chapter.Title = chapter.GetNumberTitle();
}
}