Release Shakeout (#1340)

* Fixed a bug where analyze series would not force a re-analysis. Fixed a bug where if files weren't changed since last analysis, then series word count got reset to 0.

* Fixed epub images not loading in detail drawer

* Fixed a bug on double page layout where the reader would be wonky when moving to and from mobile layout.

* package-lock.json updated

* Cleaned up some wording

* Moved a conditional on jump bar

* Bugfix for Double Page Rendering skipping pages (#1339)

* Bump versions by dotnet-bump-version.

* Double (Manga) fixes

. Fixed: *ngIf condition, last page loading Double
. Added: isLoose, pageAmount, and CanvasImageNextDouble to keep track of double sequence breaks (nn/n/w)
. Fixed: ShouldRenderReverseDouble and pageAmount conditions
. Added: Setting isLoose on loadPage()
. Added: canvasImageNextDouble in loadPage()

Co-authored-by: majora2007 <josephmajora@gmail.com>

* Added comments for Magunjun's PR.

Co-authored-by: Marcelo Guimarães Junior <75567460+magujun@users.noreply.github.com>
This commit is contained in:
Joseph Milazzo 2022-06-28 17:40:06 -05:00 committed by GitHub
parent 3c44d48e70
commit 4fd4ca6f07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 220 additions and 18701 deletions

View File

@ -273,7 +273,7 @@ namespace API.Controllers
[HttpPost("analyze")]
public ActionResult AnalyzeSeries(RefreshSeriesDto refreshSeriesDto)
{
_taskScheduler.AnalyzeFilesForSeries(refreshSeriesDto.LibraryId, refreshSeriesDto.SeriesId);
_taskScheduler.AnalyzeFilesForSeries(refreshSeriesDto.LibraryId, refreshSeriesDto.SeriesId, true);
return Ok();
}

View File

@ -40,11 +40,11 @@ public class ReaderService : IReaderService
private readonly ChapterSortComparer _chapterSortComparer = new ChapterSortComparer();
private readonly ChapterSortComparerZeroFirst _chapterSortComparerForInChapterSorting = new ChapterSortComparerZeroFirst();
public const float MinWordsPerHour = 10260F;
public const float MaxWordsPerHour = 30000F;
private const float MinWordsPerHour = 10260F;
private const float MaxWordsPerHour = 30000F;
public const float AvgWordsPerHour = (MaxWordsPerHour + MinWordsPerHour) / 2F;
public const float MinPagesPerMinute = 3.33F;
public const float MaxPagesPerMinute = 2.75F;
private const float MinPagesPerMinute = 3.33F;
private const float MaxPagesPerMinute = 2.75F;
public const float AvgPagesPerMinute = (MaxPagesPerMinute + MinPagesPerMinute) / 2F;

View File

@ -145,6 +145,7 @@ public class WordCountAnalyzerService : IWordCountAnalyzerService
private async Task ProcessSeries(Series series, bool forceUpdate = false, bool useFileName = true)
{
var isEpub = series.Format == MangaFormat.Epub;
var existingWordCount = series.WordCount;
series.WordCount = 0;
foreach (var volume in series.Volumes)
{
@ -222,6 +223,7 @@ public class WordCountAnalyzerService : IWordCountAnalyzerService
series.MinHoursToRead = seriesEstimate.MinHours;
series.MaxHoursToRead = seriesEstimate.MaxHours;
series.AvgHoursToRead = seriesEstimate.AvgHours;
if (series.WordCount == 0) series.WordCount = existingWordCount; // Restore original word count if the file hasn't changed
_unitOfWork.SeriesRepository.Update(series);
}

18835
UI/Web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
<div class="container-fluid">
<form [formGroup]="settingsForm" *ngIf="serverSettings !== undefined">
<p class="text-warning pt-2">Port and Logging Level require a manual restart of Kavita to take effect.</p>
<p class="text-warning pt-2">Port, Logging Level, and Swagger require a manual restart of Kavita to take effect.</p>
<div class="mb-3">
<label for="settings-cachedir" class="form-label">Cache Directory</label>&nbsp;<i class="fa fa-info-circle" placement="right" [ngbTooltip]="cacheDirectoryTooltip" role="button" tabindex="0"></i>
<ng-template #cacheDirectoryTooltip>Where the server place temporary files when reading. This will be cleaned up on a regular basis.</ng-template>

View File

@ -16,8 +16,8 @@
<div class="container-fluid" style="overflow: auto">
<div class="row g-0">
<div class="d-none d-md-block col-md-2 col-lg-1">
<app-image class="me-2" width="74px" [imageUrl]="chapter.coverImage"></app-image>
<div class="d-none d-md-block col-md-2 col-lg-1">
<app-image class="me-2" width="74px" [imageUrl]="coverImageUrl"></app-image>
</div>
<div class="col-md-10 col-lg-11">
<ng-container *ngIf="summary.length > 0; else noSummary">

View File

@ -2,13 +2,12 @@ import { Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NgbActiveOffcanvas } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
import { finalize, Observable, of, take, takeWhile } from 'rxjs';
import { finalize, Observable, take, takeWhile } from 'rxjs';
import { Download } from 'src/app/shared/_models/download';
import { DownloadService } from 'src/app/shared/_services/download.service';
import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.service';
import { Chapter } from 'src/app/_models/chapter';
import { ChapterMetadata } from 'src/app/_models/chapter-metadata';
import { HourEstimateRange } from 'src/app/_models/hour-estimate-range';
import { LibraryType } from 'src/app/_models/library';
import { MangaFile } from 'src/app/_models/manga-file';
import { MangaFormat } from 'src/app/_models/manga-format';
@ -51,6 +50,10 @@ export class CardDetailDrawerComponent implements OnInit {
chapters: Chapter[] = [];
imageUrls: Array<string> = [];
/**
* Cover image for the entity
*/
coverImageUrl!: string;
actions: ActionItem<any>[] = [];
@ -100,6 +103,11 @@ export class CardDetailDrawerComponent implements OnInit {
this.isChapter = this.utilityService.isChapter(this.data);
this.chapter = this.utilityService.isChapter(this.data) ? (this.data as Chapter) : (this.data as Volume).chapters[0];
if (this.isChapter) {
this.coverImageUrl = this.imageService.getChapterCoverImage(this.data.id);
} else {
this.coverImageUrl = this.imageService.getVolumeCoverImage(this.data.id);
}
this.imageUrls.push(this.imageService.getChapterCoverImage(this.chapter.id));

View File

@ -15,7 +15,6 @@
<app-metadata-filter [filterSettings]="filterSettings" [filterOpen]="filterOpen" (applyFilter)="applyMetadataFilter($event)"></app-metadata-filter>
<div class="viewport-container">
<div class="content-container">
<div class="card-container mt-2 mb-2">
<virtual-scroller #scroll [items]="items" [bufferAmount]="1" [parentScroll]="parentScroll">
<div class="grid row g-0" #container>
@ -24,7 +23,6 @@
</div>
</div>
</virtual-scroller>
<p *ngIf="items.length === 0 && !isLoading">
<ng-container [ngTemplateOutlet]="noDataTemplate"></ng-container>
@ -32,7 +30,7 @@
</div>
</div>
<ng-container [ngTemplateOutlet]="jumpBar" [ngTemplateOutletContext]="{ id: 'jumpbar' }"></ng-container>
<ng-container *ngIf="jumpBarKeysToRender.length >= 4" [ngTemplateOutlet]="jumpBar" [ngTemplateOutletContext]="{ id: 'jumpbar' }"></ng-container>
</div>
<ng-template #cardTemplate>
<virtual-scroller #scroll [items]="items" [bufferAmount]="1">
@ -55,7 +53,7 @@
</div>
<ng-template #jumpBar>
<div class="jump-bar" *ngIf="jumpBarKeysToRender.length >= 4">
<div class="jump-bar">
<ng-container *ngFor="let jumpKey of jumpBarKeysToRender; let i = index;">
<button class="btn btn-link" (click)="scrollTo(jumpKey)">
{{jumpKey.title}}

View File

@ -85,13 +85,16 @@
<ng-container *ngIf="series.format === MangaFormat.EPUB && series.wordCount > 0 || series.format !== MangaFormat.EPUB">
<div class="col-lg-1 col-md-4 col-sm-4 col-4 mb-2">
<app-icon-and-title label="Read Time" [clickable]="false" fontClasses="fa-regular fa-clock">
{{readingTime.minHours}}{{readingTime.maxHours !== readingTime.minHours ? ('-' + readingTime.maxHours) : ''}} Hour{{readingTime.minHours > 1 ? 's' : ''}}
<ng-container *ngIf="readingTime.maxHours === 0; else normalReadTime">&lt;1 Hour</ng-container>
<ng-template #normalReadTime>
{{readingTime.minHours}}{{readingTime.maxHours !== readingTime.minHours ? ('-' + readingTime.maxHours) : ''}} Hour{{readingTime.minHours > 1 ? 's' : ''}}
</ng-template>
</app-icon-and-title>
</div>
</ng-container>
<ng-container *ngIf="hasReadingProgress && showReadingTimeLeft && readingTimeLeft && readingTimeLeft.avgHours !== 0 ">
<ng-container *ngIf="hasReadingProgress && showReadingTimeLeft && readingTimeLeft && readingTimeLeft.avgHours !== 0">
<div class="vr d-none d-lg-block m-2"></div>
<div class="col-lg-1 col-md-4 col-sm-4 col-4 mb-2">
<app-icon-and-title label="Read Left" [clickable]="false" fontClasses="fa-solid fa-clock">

View File

@ -68,7 +68,7 @@
<img #image [src]="canvasImage.src" id="image-1"
class="{{getFittingOptionClass()}} {{readerMode === ReaderMode.LeftRight || readerMode === ReaderMode.UpDown ? '' : 'd-none'}} {{showClickOverlay ? 'blur' : ''}}">
<ng-container *ngIf="(ShouldRenderDoublePage || ShouldRenderReverseDouble) && (this.pageNum + 1 <= maxPages - 1 && this.pageNum > 0)">
<ng-container *ngIf="(ShouldRenderDoublePage || ShouldRenderReverseDouble) && (this.pageNum <= maxPages - 1 && this.pageNum > 0)">
<img [src]="canvasImage2.src" id="image-2" class="image-2 {{getFittingOptionClass()}} {{readerMode === ReaderMode.LeftRight || readerMode === ReaderMode.UpDown ? '' : 'd-none'}} {{showClickOverlay ? 'blur' : ''}} {{ShouldRenderReverseDouble ? 'reverse' : ''}}">
</ng-container>
</div>

View File

@ -114,6 +114,15 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
isLoading = true;
/**
* A temp variable to allow us to update isLoose.
*/
pageAmount = 0;
/**
* For double page layout, if only one page will be rendered.
*/
isLoose = false;
@ViewChild('reader') reader!: ElementRef;
@ViewChild('readingArea') readingArea!: ElementRef;
@ViewChild('content') canvas: ElementRef | undefined;
@ -128,13 +137,17 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
*/
canvasImage2 = new Image();
/**
* * * Used solely for LayoutMode.Double rendering. Will always hold the previous image in buffer.
* Used solely for LayoutMode.Double rendering. Will always hold the previous image in buffer.
*/
canvasImagePrev = new Image();
/**
* * * Used solely for LayoutMode.Double rendering. Will always hold the next image in buffer.
* Used solely for LayoutMode.Double rendering. Will always hold the next image in buffer.
*/
canvasImageNext = new Image();
/**
* Used solely for LayoutMode.DoubleReverse rendering. Will always hold the image after next in buffer.
*/
canvasImageNextDouble = new Image();
/**
* Dictates if we use render with canvas or with image. This is only for Splitting.
*/
@ -285,9 +298,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
get ShouldRenderDoublePage() {
return (
this.layoutMode !== LayoutMode.Single &&
this.layoutMode === LayoutMode.Double &&
!this.isCoverImage() &&
!this.isWideImage(this.canvasImage) &&
!this.isWideImage(this.canvasImageNext) &&
window.innerWidth > window.innerHeight // Don't render double if orientation is portrait, mostly mobile
@ -298,18 +310,25 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
return (
this.layoutMode === LayoutMode.DoubleReversed &&
!this.isCoverImage() &&
!this.isCoverImage(this.pageNum - 1) &&
!this.isWideImage(this.canvasImage) &&
!this.isWideImage(this.canvasImageNext) &&
!this.isLoose &&
window.innerWidth > window.innerHeight // Don't render double reversed if orientation is portrait, mostly mobile
);
}
@HostListener('window:resize', ['$event'])
@HostListener('window:orientationchange', ['$event'])
onResize() {
onResize() {
if (window.innerWidth > window.innerHeight) {
this.generalSettingsForm.get('layoutMode')?.enable();
return;
};
if (this.layoutMode === LayoutMode.Single || this.readerMode === ReaderMode.Webtoon) return;
if (window.innerWidth > window.innerHeight) return;
this.generalSettingsForm.get('layoutMode')?.setValue(LayoutMode.Single);
this.generalSettingsForm.get('layoutMode')?.disable();
this.toastr.info('Layout mode switched to Single due to insufficient space to render double layout');
}
@ -955,6 +974,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
pageAmount = (
!this.isCoverImage(this.pageNum - 1) &&
!this.isWideImage(this.canvasImagePrev) &&
!this.isWideImage(this.canvasImageNextDouble) &&
!this.isSecondLastImage() &&
!this.isLastImage()
? 2 : 1);
@ -972,6 +992,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.pagingDirection = PAGING_DIRECTION.FORWARD;
if (this.isNoSplit() || notInSplit) {
this.setPageNum(this.pageNum + pageAmount);
this.pageAmount = pageAmount;
if (this.readerMode !== ReaderMode.Webtoon) {
this.canvasImage.src = this.getPageUrl(this.pageNum);
@ -1002,7 +1023,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
pageAmount = (
!this.isCoverImage() &&
!this.isCoverImage(this.pageNum - 1) &&
!this.isWideImage() &&
!this.isWideImage(this.canvasImage) &&
!this.isWideImage(this.canvasImageNext)
? 2 : 1);
}
@ -1233,11 +1254,13 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
loadPage() {
this.isLoading = true;
this.canvasImage2.src = '';
this.isLoose = (this.pageAmount === 1 ? true : false);
this.canvasImage.src = this.getPageUrl(this.pageNum);
if (this.layoutMode !== LayoutMode.Single) {
this.canvasImagePrev.src = this.getPageUrl(this.pageNum + (this.layoutMode !== LayoutMode.DoubleReversed ? - 1 : + 1));
this.canvasImageNext.src = this.getPageUrl(this.pageNum + (this.layoutMode !== LayoutMode.DoubleReversed ? + 1 : - 1));
this.canvasImageNextDouble.src = this.getPageUrl(this.pageNum + 2);
if (this.ShouldRenderDoublePage || this.ShouldRenderReverseDouble) {
this.canvasImage2.src = this.canvasImageNext.src;
}

View File

@ -1,5 +1,3 @@
<img #img class="lazyload" [src]="imageService.placeholderImage" [attr.data-src]="imageUrl"
(error)="imageService.updateErroredImage($event)"
aria-hidden="true">
<!-- TODO: Need to think about how to not lazyload but also handle broken images-->
<!-- <img #img [src]="imageUrl" aria-hidden="true"> -->
aria-hidden="true">