diff --git a/API/API.csproj b/API/API.csproj
index 80f371b7a..1ddb37d7f 100644
--- a/API/API.csproj
+++ b/API/API.csproj
@@ -80,7 +80,6 @@
-
diff --git a/API/Helpers/PersonHelper.cs b/API/Helpers/PersonHelper.cs
index fe437daa8..07161e418 100644
--- a/API/Helpers/PersonHelper.cs
+++ b/API/Helpers/PersonHelper.cs
@@ -80,7 +80,6 @@ public static class PersonHelper
// If not, create a new Person entity using the real name
dbPerson = new PersonBuilder(personName).Build();
peopleToAttach.Add(dbPerson); // Add new person to the list to be attached
- modification = true;
}
// Add the person to the SeriesMetadataPeople collection
diff --git a/API/Services/BookService.cs b/API/Services/BookService.cs
index 2eee99891..99fdd1400 100644
--- a/API/Services/BookService.cs
+++ b/API/Services/BookService.cs
@@ -399,11 +399,14 @@ public class BookService : IBookService
{
// Check if any classes on the html node (some r2l books do this) and move them to body tag for scoping
var htmlNode = doc.DocumentNode.SelectSingleNode("//html");
- if (htmlNode == null || !htmlNode.Attributes.Contains("class")) return body.InnerHtml;
+ if (htmlNode == null) return body.InnerHtml;
var bodyClasses = body.Attributes.Contains("class") ? body.Attributes["class"].Value : string.Empty;
- var classes = htmlNode.Attributes["class"].Value + " " + bodyClasses;
- body.Attributes.Add("class", $"{classes}");
+ var htmlClasses = htmlNode.Attributes.Contains("class") ? htmlNode.Attributes["class"].Value : string.Empty;
+
+ body.Attributes.Add("class", $"{htmlClasses} {bodyClasses}");
+
+
// I actually need the body tag itself for the classes, so i will create a div and put the body stuff there.
return $"
{body.InnerHtml}
";
}
diff --git a/API/Services/CacheService.cs b/API/Services/CacheService.cs
index a2acc538b..283d4b1ac 100644
--- a/API/Services/CacheService.cs
+++ b/API/Services/CacheService.cs
@@ -80,7 +80,6 @@ public class CacheService : ICacheService
///
public IEnumerable GetCachedFileDimensions(string cachePath)
{
- var sw = Stopwatch.StartNew();
var files = _directoryService.GetFilesWithExtension(cachePath, Tasks.Scanner.Parser.Parser.ImageFileExtensions)
.OrderByNatural(Path.GetFileNameWithoutExtension)
.ToArray();
@@ -186,8 +185,15 @@ public class CacheService : ICacheService
}
else
{
- // Potential BUG: If the folder is left here and there are no files within, this could theoretically return without proper cache
- return chapter;
+ // Do an explicit check for files since rarely a "permission denied" error on deleting
+ // the file can occur, thus leaving an empty folder and we would never re-cache the files.
+ if (_directoryService.GetFiles(extractPath).Any())
+ {
+ return chapter;
+ }
+
+ // Delete the extractPath as ExtractArchive will return if the directory already exists
+ _directoryService.ClearAndDeleteDirectory(extractPath);
}
}
@@ -210,13 +216,13 @@ public class CacheService : ICacheService
///
public void ExtractChapterFiles(string extractPath, IReadOnlyList? files, bool extractPdfImages = false)
{
- if (files == null) return;
+ if (files == null || files.Count == 0) return;
var removeNonImages = true;
var fileCount = files.Count;
var extraPath = string.Empty;
var extractDi = _directoryService.FileSystem.DirectoryInfo.New(extractPath);
- if (files.Count > 0 && files[0].Format == MangaFormat.Image)
+ if (files[0].Format == MangaFormat.Image)
{
// Check if all the files are Images. If so, do a directory copy, else do the normal copy
if (files.All(f => f.Format == MangaFormat.Image))
diff --git a/API/Services/Plus/ExternalMetadataService.cs b/API/Services/Plus/ExternalMetadataService.cs
index 424331e09..aef97bdda 100644
--- a/API/Services/Plus/ExternalMetadataService.cs
+++ b/API/Services/Plus/ExternalMetadataService.cs
@@ -113,7 +113,7 @@ public class ExternalMetadataService : IExternalMetadataService
public async Task FetchExternalDataTask()
{
// Find all Series that are eligible and limit
- var ids = await _unitOfWork.ExternalSeriesMetadataRepository.GetSeriesThatNeedExternalMetadata(25, false);
+ var ids = await _unitOfWork.ExternalSeriesMetadataRepository.GetSeriesThatNeedExternalMetadata(25);
if (ids.Count == 0) return;
ids = await _unitOfWork.ExternalSeriesMetadataRepository.GetSeriesThatNeedExternalMetadata(25, true);
@@ -444,7 +444,7 @@ public class ExternalMetadataService : IExternalMetadataService
{
if (errorMessage.Contains("Too many Requests"))
{
- _logger.LogInformation("Hit rate limit, will retry in 3 seconds");
+ _logger.LogDebug("Hit rate limit, will retry in 3 seconds");
await Task.Delay(3000);
result = await (Configuration.KavitaPlusApiUrl + "/api/metadata/v2/series-detail")
@@ -673,7 +673,7 @@ public class ExternalMetadataService : IExternalMetadataService
foreach (var relation in externalMetadataRelations.Where(r => r.Relation != RelationKind.Parent))
{
- var names = new [] {relation.SeriesName.PreferredTitle, relation.SeriesName.RomajiTitle, relation.SeriesName.EnglishTitle, relation.SeriesName.NativeTitle};
+ List names = new [] {relation.SeriesName.PreferredTitle, relation.SeriesName.RomajiTitle, relation.SeriesName.EnglishTitle, relation.SeriesName.NativeTitle}.Where(s => !string.IsNullOrEmpty(s)).ToList()!;
var relatedSeries = await _unitOfWork.SeriesRepository.GetSeriesByAnyName(
names,
relation.PlusMediaFormat.GetMangaFormats(),
@@ -1171,6 +1171,14 @@ public class ExternalMetadataService : IExternalMetadataService
return false;
}
+ // Some publishers (CBR) can be represented as Boom! Studios/Boom! Town imprint, so let's handle that appropriately
+ if (publisher.Contains('/') || publisher.Contains("imprint", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var imprint = publisher.Split('/')[1].Replace("imprint", string.Empty);
+ return await UpdateChapterPeople(chapter, settings, PersonRole.Publisher, [publisher]) ||
+ await UpdateChapterPeople(chapter, settings, PersonRole.Imprint, [imprint]);
+ }
+
return await UpdateChapterPeople(chapter, settings, PersonRole.Publisher, [publisher]);
}
@@ -1224,7 +1232,7 @@ public class ExternalMetadataService : IExternalMetadataService
.DistinctBy(p => Parser.Normalize(p.Name))
.ToList();
- await PersonHelper.UpdateChapterPeopleAsync(chapter, staff, role, _unitOfWork);
+ await PersonHelper.UpdateChapterPeopleAsync(chapter, staff ?? [], role, _unitOfWork);
foreach (var person in chapter.People.Where(p => p.Role == role))
{
diff --git a/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.html b/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.html
index 85cd9bc8f..56a3488f3 100644
--- a/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.html
+++ b/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.html
@@ -7,7 +7,12 @@
}