From d750ce77a05e3189467facc4d88219077cc07aab Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Sun, 3 Oct 2021 05:27:35 -0700 Subject: [PATCH] Shakeout Bugs (#623) * Fixed an issue where ScanSeries would not fetch all the entities and thus files would get duplicated on the Chapter * Remove building extra language binaries on build. * Fixed an issue where first scan would cause an issue with websocket due to trying to send NaN over the wire. * Fixed an issue where on new scans scan in progress indicators wouldn't turn off due to the way we were consuming events off the pipe. * Ensure login page doesn't flash on first load * Don't process touch events at all unless selection is enabled. --- API/API.csproj | 4 ++ .../ApplicationServiceExtensions.cs | 2 +- API/Services/Tasks/ScannerService.cs | 16 +++-- API/SignalR/MessageFactory.cs | 3 +- .../manage-library.component.ts | 5 +- .../cards/card-item/card-item.component.ts | 2 + .../series-card/series-card.component.ts | 19 +++-- .../library-detail.component.ts | 15 ++-- .../app/user-login/user-login.component.html | 72 ++++++++++--------- .../app/user-login/user-login.component.ts | 6 ++ 10 files changed, 90 insertions(+), 54 deletions(-) diff --git a/API/API.csproj b/API/API.csproj index 55163ad78..d4cfe4316 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -16,6 +16,10 @@ bin\Debug\API.xml + + en + + Kavita diff --git a/API/Extensions/ApplicationServiceExtensions.cs b/API/Extensions/ApplicationServiceExtensions.cs index 3d7fde699..cd5a621fc 100644 --- a/API/Extensions/ApplicationServiceExtensions.cs +++ b/API/Extensions/ApplicationServiceExtensions.cs @@ -42,7 +42,7 @@ namespace API.Extensions services.AddSqLite(config, env); services.AddLogging(config); - services.AddSignalR(); + services.AddSignalR(opt => opt.EnableDetailedErrors = true); } private static void AddSqLite(this IServiceCollection services, IConfiguration config, diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs index 87da418ad..0701fe13f 100644 --- a/API/Services/Tasks/ScannerService.cs +++ b/API/Services/Tasks/ScannerService.cs @@ -52,7 +52,7 @@ namespace API.Services.Tasks { var sw = new Stopwatch(); var files = await _unitOfWork.SeriesRepository.GetFilesForSeries(seriesId); - var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId); + var series = await _unitOfWork.SeriesRepository.GetFullSeriesForSeriesIdAsync(seriesId); var chapterIds = await _unitOfWork.SeriesRepository.GetChapterIdsForSeriesAsync(new[] {seriesId}); var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(libraryId, LibraryIncludes.Folders); var folderPaths = library.Folders.Select(f => f.Path).ToList(); @@ -186,7 +186,7 @@ namespace API.Services.Tasks _logger.LogInformation("[ScannerService] Beginning file scan on {LibraryName}", library.Name); await _messageHub.Clients.All.SendAsync(SignalREvents.ScanLibraryProgress, - MessageFactory.ScanLibraryProgressEvent(libraryId, 0, string.Empty)); + MessageFactory.ScanLibraryProgressEvent(libraryId, 0)); var scanner = new ParseScannedFiles(_bookService, _logger); var series = scanner.ScanLibrariesForSeries(library.Type, library.Folders.Select(fp => fp.Path), out var totalFiles, out var scanElapsedTime); @@ -217,7 +217,7 @@ namespace API.Services.Tasks BackgroundJob.Enqueue(() => _metadataService.RefreshMetadata(libraryId, false)); await _messageHub.Clients.All.SendAsync(SignalREvents.ScanLibraryProgress, - MessageFactory.ScanLibraryProgressEvent(libraryId, 100, string.Empty)); + MessageFactory.ScanLibraryProgressEvent(libraryId, 100)); } /// @@ -253,6 +253,7 @@ namespace API.Services.Tasks _logger.LogDebug("[ScannerService] Updating existing series"); for (var chunk = 0; chunk <= chunkInfo.TotalChunks; chunk++) { + if (chunkInfo.TotalChunks == 0) continue; totalTime += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); _logger.LogDebug($"[ScannerService] Processing chunk {chunk} / {chunkInfo.TotalChunks} with size {chunkInfo.ChunkSize} Series ({chunk * chunkInfo.ChunkSize} - {(chunk + 1) * chunkInfo.ChunkSize}"); @@ -298,8 +299,9 @@ namespace API.Services.Tasks await _messageHub.Clients.All.SendAsync(SignalREvents.SeriesRemoved, MessageFactory.SeriesRemovedEvent(missing.Id, missing.Name, library.Id)); } + var progress = Math.Max(0, Math.Min(100, ((chunk + 1F) * chunkInfo.ChunkSize) / chunkInfo.TotalSize)); await _messageHub.Clients.All.SendAsync(SignalREvents.ScanLibraryProgress, - MessageFactory.ScanLibraryProgressEvent(library.Id, ((chunk + 1F) * chunkInfo.ChunkSize) / chunkInfo.TotalSize, string.Empty)); + MessageFactory.ScanLibraryProgressEvent(library.Id, progress)); } @@ -336,6 +338,7 @@ namespace API.Services.Tasks newSeries.Add(existingSeries); } + var i = 0; foreach(var series in newSeries) { try @@ -353,6 +356,9 @@ namespace API.Services.Tasks // Inform UI of new series added await _messageHub.Clients.All.SendAsync(SignalREvents.SeriesAdded, MessageFactory.SeriesAddedEvent(series.Id, series.Name, library.Id)); + var progress = Math.Max(0F, Math.Min(100F, i * 1F / newSeries.Count)); + await _messageHub.Clients.All.SendAsync(SignalREvents.ScanLibraryProgress, + MessageFactory.ScanLibraryProgressEvent(library.Id, progress)); } else { @@ -360,6 +366,8 @@ namespace API.Services.Tasks _logger.LogCritical( "[ScannerService] There was a critical error that resulted in a failed scan. Please check logs and rescan"); } + + i++; } catch (Exception ex) { diff --git a/API/SignalR/MessageFactory.cs b/API/SignalR/MessageFactory.cs index ebc1b3e83..a8ed729f4 100644 --- a/API/SignalR/MessageFactory.cs +++ b/API/SignalR/MessageFactory.cs @@ -59,7 +59,7 @@ namespace API.SignalR }; } - public static SignalRMessage ScanLibraryProgressEvent(int libraryId, float progress, string seriesName) + public static SignalRMessage ScanLibraryProgressEvent(int libraryId, float progress) { return new SignalRMessage() { @@ -68,7 +68,6 @@ namespace API.SignalR { LibraryId = libraryId, Progress = progress, - SeriesName = seriesName } }; } diff --git a/UI/Web/src/app/admin/manage-library/manage-library.component.ts b/UI/Web/src/app/admin/manage-library/manage-library.component.ts index 09edc72f8..36d5db3f9 100644 --- a/UI/Web/src/app/admin/manage-library/manage-library.component.ts +++ b/UI/Web/src/app/admin/manage-library/manage-library.component.ts @@ -36,9 +36,12 @@ export class ManageLibraryComponent implements OnInit, OnDestroy { this.getLibraries(); // when a progress event comes in, show it on the UI next to library - this.hubService.messages$.pipe(takeWhile(event => event.event === EVENTS.ScanLibraryProgress)).subscribe((event) => { + this.hubService.messages$.pipe(takeUntil(this.onDestroy)).subscribe((event) => { + if (event.event != EVENTS.ScanLibraryProgress) return; + const scanEvent = event.payload as ScanLibraryProgressEvent; this.scanInProgress[scanEvent.libraryId] = scanEvent.progress !== 100; + if (this.scanInProgress[scanEvent.libraryId] === false && scanEvent.progress === 100) { this.libraryService.getLibraries().pipe(take(1)).subscribe(libraries => { const newLibrary = libraries.find(lib => lib.id === scanEvent.libraryId); diff --git a/UI/Web/src/app/cards/card-item/card-item.component.ts b/UI/Web/src/app/cards/card-item/card-item.component.ts index 2f1633b4a..b0974ba5a 100644 --- a/UI/Web/src/app/cards/card-item/card-item.component.ts +++ b/UI/Web/src/app/cards/card-item/card-item.component.ts @@ -123,6 +123,7 @@ export class CardItemComponent implements OnInit, OnDestroy { prevOffset: number = 0; @HostListener('touchstart', ['$event']) onTouchStart(event: TouchEvent) { + if (!this.allowSelection) return; const verticalOffset = (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0); @@ -133,6 +134,7 @@ export class CardItemComponent implements OnInit, OnDestroy { @HostListener('touchend', ['$event']) onTouchEnd(event: TouchEvent) { + if (!this.allowSelection) return; const delta = event.timeStamp - this.prevTouchTime; const verticalOffset = (window.pageYOffset || document.documentElement.scrollTop diff --git a/UI/Web/src/app/cards/series-card/series-card.component.ts b/UI/Web/src/app/cards/series-card/series-card.component.ts index 47258649a..11d869401 100644 --- a/UI/Web/src/app/cards/series-card/series-card.component.ts +++ b/UI/Web/src/app/cards/series-card/series-card.component.ts @@ -1,8 +1,8 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; import { Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ToastrService } from 'ngx-toastr'; -import { take, takeWhile } from 'rxjs/operators'; +import { take, takeUntil, takeWhile } from 'rxjs/operators'; import { Series } from 'src/app/_models/series'; import { AccountService } from 'src/app/_services/account.service'; import { ImageService } from 'src/app/_services/image.service'; @@ -13,13 +13,14 @@ import { ActionService } from 'src/app/_services/action.service'; import { EditSeriesModalComponent } from '../_modals/edit-series-modal/edit-series-modal.component'; import { RefreshMetadataEvent } from 'src/app/_models/events/refresh-metadata-event'; import { MessageHubService } from 'src/app/_services/message-hub.service'; +import { Subject } from 'rxjs'; @Component({ selector: 'app-series-card', templateUrl: './series-card.component.html', styleUrls: ['./series-card.component.scss'] }) -export class SeriesCardComponent implements OnInit, OnChanges { +export class SeriesCardComponent implements OnInit, OnChanges, OnDestroy { @Input() data!: Series; @Input() libraryId = 0; @Input() suppressLibraryLink = false; @@ -43,6 +44,7 @@ export class SeriesCardComponent implements OnInit, OnChanges { isAdmin = false; actions: ActionItem[] = []; imageUrl: string = ''; + onDestroy: Subject = new Subject(); constructor(private accountService: AccountService, private router: Router, private seriesService: SeriesService, private toastr: ToastrService, @@ -61,12 +63,10 @@ export class SeriesCardComponent implements OnInit, OnChanges { if (this.data) { this.imageUrl = this.imageService.randomize(this.imageService.getSeriesCoverImage(this.data.id)); - this.hubService.refreshMetadata.pipe(takeWhile(event => event.libraryId === this.libraryId)).subscribe((event: RefreshMetadataEvent) => { + this.hubService.refreshMetadata.pipe(takeWhile(event => event.libraryId === this.libraryId), takeUntil(this.onDestroy)).subscribe((event: RefreshMetadataEvent) => { if (this.data.id === event.seriesId) { this.imageUrl = this.imageService.randomize(this.imageService.getSeriesCoverImage(this.data.id)); - console.log('Refresh event came through, updating cover image'); - } - + } }); } } @@ -78,6 +78,11 @@ export class SeriesCardComponent implements OnInit, OnChanges { } } + ngOnDestroy() { + this.onDestroy.next(); + this.onDestroy.complete(); + } + handleSeriesActionCallback(action: Action, series: Series) { switch (action) { case(Action.MarkAsRead): diff --git a/UI/Web/src/app/library-detail/library-detail.component.ts b/UI/Web/src/app/library-detail/library-detail.component.ts index f192664c1..57c85d789 100644 --- a/UI/Web/src/app/library-detail/library-detail.component.ts +++ b/UI/Web/src/app/library-detail/library-detail.component.ts @@ -1,7 +1,8 @@ -import { Component, HostListener, OnInit } from '@angular/core'; +import { Component, HostListener, OnDestroy, OnInit } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; -import { debounceTime, take, takeWhile } from 'rxjs/operators'; +import { Subject } from 'rxjs'; +import { debounceTime, take, takeUntil, takeWhile } from 'rxjs/operators'; import { BulkSelectionService } from '../cards/bulk-selection.service'; import { UpdateFilterEvent } from '../cards/card-detail-layout/card-detail-layout.component'; import { KEY_CODES } from '../shared/_services/utility.service'; @@ -21,7 +22,7 @@ import { SeriesService } from '../_services/series.service'; templateUrl: './library-detail.component.html', styleUrls: ['./library-detail.component.scss'] }) -export class LibraryDetailComponent implements OnInit { +export class LibraryDetailComponent implements OnInit, OnDestroy { libraryId!: number; libraryName = ''; @@ -33,6 +34,7 @@ export class LibraryDetailComponent implements OnInit { filter: SeriesFilter = { mangaFormat: null }; + onDestroy: Subject = new Subject(); bulkActionCallback = (action: Action, data: any) => { const selectedSeriesIndexies = this.bulkSelectionService.getSelectedCardsForSource('series'); @@ -80,11 +82,16 @@ export class LibraryDetailComponent implements OnInit { } ngOnInit(): void { - this.hubService.seriesAdded.pipe(takeWhile(event => event.libraryId === this.libraryId), debounceTime(6000)).subscribe((event: SeriesAddedEvent) => { + this.hubService.seriesAdded.pipe(takeWhile(event => event.libraryId === this.libraryId), debounceTime(6000), takeUntil(this.onDestroy)).subscribe((event: SeriesAddedEvent) => { this.loadPage(); }); } + ngOnDestroy() { + this.onDestroy.next(); + this.onDestroy.complete(); + } + @HostListener('document:keydown.shift', ['$event']) handleKeypress(event: KeyboardEvent) { if (event.key === KEY_CODES.SHIFT) { diff --git a/UI/Web/src/app/user-login/user-login.component.html b/UI/Web/src/app/user-login/user-login.component.html index 8b505d184..bd2d6c048 100644 --- a/UI/Web/src/app/user-login/user-login.component.html +++ b/UI/Web/src/app/user-login/user-login.component.html @@ -1,44 +1,46 @@