Small set of Fixes (#3776)

This commit is contained in:
Joe Milazzo 2025-04-26 12:32:56 -06:00 committed by GitHub
parent 5c06e14a73
commit 7b3198ed9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 37 additions and 16 deletions

View File

@ -80,7 +80,6 @@
<PackageReference Include="Nager.ArticleNumber" Version="1.0.7" />
<PackageReference Include="NetVips" Version="3.0.0" />
<PackageReference Include="NetVips.Native" Version="8.16.1" />
<PackageReference Include="NReco.Logging.File" Version="1.2.2" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />

View File

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

View File

@ -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 $"<div class=\"{body.Attributes["class"].Value}\">{body.InnerHtml}</div>";
}

View File

@ -80,7 +80,6 @@ public class CacheService : ICacheService
/// <returns></returns>
public IEnumerable<FileDimensionDto> 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
/// <returns></returns>
public void ExtractChapterFiles(string extractPath, IReadOnlyList<MangaFile>? 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))

View File

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

View File

@ -7,7 +7,12 @@
<app-card-actionables [actions]="actionables" (actionHandler)="performAction($event)"></app-card-actionables>
}
<h4 class="header" (click)="sectionClicked($event)" [ngClass]="{'non-selectable': !clickableTitle}">
<a [href]="titleLink !== '' ? titleLink : 'javascript:void(0)'" class="section-title">{{title}}</a>
@if (titleLink !== '') {
<a [href]="titleLink | safeUrl" class="section-title">{{title}}</a>
} @else {
<a href="javascript:void(0)" class="section-title">{{title}}</a>
}
@if (iconClasses !== '') {
<i class="{{iconClasses}} title-icon ms-1" aria-hidden="true"></i>
}

View File

@ -15,13 +15,14 @@ import {NgClass, NgTemplateOutlet} from '@angular/common';
import {TranslocoDirective} from "@jsverse/transloco";
import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component";
import {ActionItem} from "../../../_services/action-factory.service";
import {SafeUrlPipe} from "../../../_pipes/safe-url.pipe";
@Component({
selector: 'app-carousel-reel',
templateUrl: './carousel-reel.component.html',
styleUrls: ['./carousel-reel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgClass, SwiperModule, NgTemplateOutlet, TranslocoDirective, CardActionablesComponent]
imports: [NgClass, SwiperModule, NgTemplateOutlet, TranslocoDirective, CardActionablesComponent, SafeUrlPipe]
})
export class CarouselReelComponent {