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

- {{title}} + @if (titleLink !== '') { + {{title}} + } @else { + {{title}} + } + @if (iconClasses !== '') { } diff --git a/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.ts b/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.ts index 043b87482..39975022b 100644 --- a/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.ts +++ b/UI/Web/src/app/carousel/_components/carousel-reel/carousel-reel.component.ts @@ -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 {