Release Testing Day 3 (#1496)

* Tweaked log messaging for library scan when no files were scanned.

* When a theme that is set gets removed due to a scan, inform the user to refresh.

* Fixed a typo and make Darkness -> Brightness

* Make download theme files allowed to be invoked by non-authenticated users, to allow new users to get the default theme.

* Hide all series side nav item if there are no libraries exposed to the user

* Fixed an API for Tachiyomi when syncing progress

* Fixed dashboard not responding to Series Removed and Added events.

Ensure we send SeriesRemoved events when they are deleted.

* Reverted Hangfire SQLite due to aborted jobs being resumed, when they shouldnt. Fixed some scan loop issues where cover gen wouldn't be invoked always on new libraries.
This commit is contained in:
Joseph Milazzo 2022-09-01 13:45:34 -05:00 committed by GitHub
parent ab21c1b49e
commit f2249ea39b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 58 additions and 28 deletions

View File

@ -50,7 +50,7 @@ public class TachiyomiController : BaseApiController
if (prevChapterId == -1)
{
var series = await _unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId);
var userHasProgress = series.PagesRead == 0 || series.PagesRead < series.Pages;
var userHasProgress = series.PagesRead != 0 && series.PagesRead < series.Pages;
// If the user doesn't have progress, then return null, which the extension will catch as 204 (no content) and report nothing as read
if (!userHasProgress) return null;

View File

@ -51,6 +51,7 @@ public class ThemeController : BaseApiController
/// Returns css content to the UI. UI is expected to escape the content
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("download-content")]
public async Task<ActionResult<string>> GetThemeContent(int themeId)
{

View File

@ -122,7 +122,7 @@ public interface ISeriesRepository
Task<Series> GetSeriesByFolderPath(string folder);
Task<Series> GetFullSeriesByName(string series, int libraryId);
Task<Series> GetFullSeriesByAnyName(string seriesName, string localizedName, int libraryId, MangaFormat format, bool withFullIncludes = true);
Task RemoveSeriesNotInList(IList<ParsedSeries> seenSeries, int libraryId);
Task<List<Series>> RemoveSeriesNotInList(IList<ParsedSeries> seenSeries, int libraryId);
Task<IDictionary<string, IList<SeriesModified>>> GetFolderPathMap(int libraryId);
}
@ -230,6 +230,7 @@ public class SeriesRepository : ISeriesRepository
{
return await _context.Series
.Where(s => s.Id == seriesId)
.Include(s => s.Relations)
.Include(s => s.Metadata)
.ThenInclude(m => m.People)
.Include(s => s.Metadata)
@ -1274,9 +1275,9 @@ public class SeriesRepository : ISeriesRepository
/// </summary>
/// <param name="seenSeries"></param>
/// <param name="libraryId"></param>
public async Task RemoveSeriesNotInList(IList<ParsedSeries> seenSeries, int libraryId)
public async Task<List<Series>> RemoveSeriesNotInList(IList<ParsedSeries> seenSeries, int libraryId)
{
if (seenSeries.Count == 0) return;
if (seenSeries.Count == 0) return new List<Series>();
var ids = new List<int>();
foreach (var parsedSeries in seenSeries)
{
@ -1289,7 +1290,6 @@ public class SeriesRepository : ISeriesRepository
{
ids.Add(series);
}
}
var seriesToRemove = await _context.Series
@ -1298,6 +1298,8 @@ public class SeriesRepository : ISeriesRepository
.ToListAsync();
_context.Series.RemoveRange(seriesToRemove);
return seriesToRemove;
}
public async Task<PagedList<SeriesDto>> GetHighlyRated(int userId, int libraryId, UserParams userParams)

View File

@ -36,8 +36,8 @@ public interface IMetadataService
/// <param name="libraryId"></param>
/// <param name="seriesId"></param>
/// <param name="forceUpdate">Overrides any cache logic and forces execution</param>
Task GenerateCoversForSeries(int libraryId, int seriesId, bool forceUpdate = true);
Task GenerateCoversForSeries(int libraryId, int seriesId, bool forceUpdate = true);
Task GenerateCoversForSeries(Series series, bool forceUpdate = false);
Task RemoveAbandonedMetadataKeys();
}

View File

@ -171,7 +171,16 @@ public class ProcessSeries : IProcessSeries
await _eventHub.SendMessageAsync(MessageFactory.Error,
MessageFactory.ErrorEvent($"There was an issue writing to the DB for Series {series}",
ex.Message));
return;
}
if (seriesAdded)
{
await _eventHub.SendMessageAsync(MessageFactory.SeriesAdded,
MessageFactory.SeriesAddedEvent(series.Id, series.Name, series.LibraryId), false);
}
_logger.LogInformation("[ScannerService] Finished series update on {SeriesName} in {Milliseconds} ms", seriesName, scanWatch.ElapsedMilliseconds);
}
}
catch (Exception ex)
@ -179,13 +188,7 @@ public class ProcessSeries : IProcessSeries
_logger.LogError(ex, "[ScannerService] There was an exception updating series for {SeriesName}", series.Name);
}
if (seriesAdded)
{
await _eventHub.SendMessageAsync(MessageFactory.SeriesAdded,
MessageFactory.SeriesAddedEvent(series.Id, series.Name, series.LibraryId));
}
_logger.LogInformation("[ScannerService] Finished series update on {SeriesName} in {Milliseconds} ms", seriesName, scanWatch.ElapsedMilliseconds);
await _metadataService.GenerateCoversForSeries(series, false);
EnqueuePostSeriesProcessTasks(series.LibraryId, series.Id);
}
@ -213,7 +216,7 @@ public class ProcessSeries : IProcessSeries
public void EnqueuePostSeriesProcessTasks(int libraryId, int seriesId, bool forceUpdate = false)
{
BackgroundJob.Enqueue(() => _metadataService.GenerateCoversForSeries(libraryId, seriesId, forceUpdate));
//BackgroundJob.Enqueue(() => _metadataService.GenerateCoversForSeries(libraryId, seriesId, forceUpdate));
BackgroundJob.Enqueue(() => _wordCountAnalyzerService.ScanSeries(libraryId, seriesId, forceUpdate));
}

View File

@ -205,7 +205,7 @@ public class ScannerService : IScannerService
var scanElapsedTime = await ScanFiles(library, new []{folderPath}, false, TrackFiles, true);
_logger.LogInformation("ScanFiles for {Series} took {Time}", series.Name, scanElapsedTime);
await Task.WhenAll(processTasks);
//await Task.WhenAll(processTasks);
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.LibraryScanProgressEvent(library.Name, ProgressEventType.Ended, series.Name));
@ -475,14 +475,29 @@ public class ScannerService : IScannerService
// Could I delete anything in a Library's Series where the LastScan date is before scanStart?
// NOTE: This implementation is expensive
await _unitOfWork.SeriesRepository.RemoveSeriesNotInList(seenSeries, library.Id);
var removedSeries = await _unitOfWork.SeriesRepository.RemoveSeriesNotInList(seenSeries, library.Id);
_unitOfWork.LibraryRepository.Update(library);
if (await _unitOfWork.CommitAsync())
{
_logger.LogInformation(
"[ScannerService] Finished scan of {TotalFiles} files and {ParsedSeriesCount} series in {ElapsedScanTime} milliseconds for {LibraryName}",
totalFiles, seenSeries.Count, sw.ElapsedMilliseconds, library.Name);
if (totalFiles == 0)
{
_logger.LogInformation(
"[ScannerService] Finished library scan of {ParsedSeriesCount} series in {ElapsedScanTime} milliseconds for {LibraryName}. There were no changes",
totalFiles, seenSeries.Count, sw.ElapsedMilliseconds, library.Name);
}
else
{
_logger.LogInformation(
"[ScannerService] Finished library scan of {TotalFiles} files and {ParsedSeriesCount} series in {ElapsedScanTime} milliseconds for {LibraryName}",
totalFiles, seenSeries.Count, sw.ElapsedMilliseconds, library.Name);
}
foreach (var s in removedSeries)
{
await _eventHub.SendMessageAsync(MessageFactory.SeriesRemoved,
MessageFactory.SeriesRemovedEvent(s.Id, s.Name, s.LibraryId), false);
}
}
else
{

View File

@ -156,7 +156,7 @@ namespace API
services.AddHangfire(configuration => configuration
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSQLiteStorage());
.UseMemoryStorage()); // UseSQLiteStorage - SQLite has some issues around resuming jobs when aborted
// Add the processing server as IHostedService
services.AddHangfireServer(options =>

View File

@ -2,7 +2,8 @@ import { DOCUMENT } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable, OnDestroy, Renderer2, RendererFactory2, SecurityContext } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { map, ReplaySubject, Subject, takeUntil } from 'rxjs';
import { ToastrService } from 'ngx-toastr';
import { map, ReplaySubject, Subject, takeUntil, take } from 'rxjs';
import { environment } from 'src/environments/environment';
import { ConfirmService } from '../shared/confirm.service';
import { NotificationProgressEvent } from '../_models/events/notification-progress-event';
@ -35,7 +36,7 @@ export class ThemeService implements OnDestroy {
constructor(rendererFactory: RendererFactory2, @Inject(DOCUMENT) private document: Document, private httpClient: HttpClient,
messageHub: MessageHubService, private domSantizer: DomSanitizer, private confirmService: ConfirmService) {
messageHub: MessageHubService, private domSantizer: DomSanitizer, private confirmService: ConfirmService, private toastr: ToastrService) {
this.renderer = rendererFactory.createRenderer(null, null);
this.getThemes();
@ -47,7 +48,9 @@ export class ThemeService implements OnDestroy {
if (notificationEvent.name !== EVENTS.SiteThemeProgress) return;
if (notificationEvent.eventType === 'ended') {
if (notificationEvent.name === EVENTS.SiteThemeProgress) this.getThemes().subscribe(() => {});
if (notificationEvent.name === EVENTS.SiteThemeProgress) this.getThemes().subscribe(() => {
});
}
});
}
@ -73,6 +76,12 @@ export class ThemeService implements OnDestroy {
return this.httpClient.get<SiteTheme[]>(this.baseUrl + 'theme').pipe(map(themes => {
this.themeCache = themes;
this.themesSource.next(themes);
this.currentTheme$.pipe(take(1)).subscribe(theme => {
if (!themes.includes(theme)) {
this.setTheme(this.defaultTheme);
this.toastr.info('The active theme no longer exists. Please refresh the page.');
}
});
return themes;
}));
}

View File

@ -57,7 +57,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
this.seriesService.getSeries(seriesAddedEvent.seriesId).subscribe(series => {
this.recentlyAddedSeries.unshift(series);
this.cdRef.markForCheck();
this.cdRef.detectChanges();
});
} else if (res.event === EVENTS.SeriesRemoved) {
const seriesRemovedEvent = res.payload as SeriesRemovedEvent;

View File

@ -218,7 +218,7 @@
</div>
<div class="row mb-2">
<div class="col-md-6 col-sm-12">
<label for="darkness" class="form-label range-label">Darkess</label>
<label for="darkness" class="form-label range-label">Brightness</label>
<input type="range" class="form-range" id="darkness"
min="10" max="100" step="1" formControlName="darkness">
<span class="range-text">{{generalSettingsForm.get('darkness')?.value + '%'}}</span>

View File

@ -12,7 +12,7 @@
<app-side-nav-item icon="fa-list" title="Collections" link="/collections/"></app-side-nav-item>
<app-side-nav-item icon="fa-list-ol" title="Reading Lists" link="/lists/"></app-side-nav-item>
<app-side-nav-item icon="fa-bookmark" title="Bookmarks" link="/bookmarks/"></app-side-nav-item>
<app-side-nav-item icon="fa-regular fa-rectangle-list" title="All Series" link="/all-series/"></app-side-nav-item>
<app-side-nav-item icon="fa-regular fa-rectangle-list" title="All Series" link="/all-series/" *ngIf="libraries.length > 0"></app-side-nav-item>
<div class="mb-2 mt-3 ms-2 me-2" *ngIf="libraries.length > 10 && !(navService?.sideNavCollapsed$ | async)">
<label for="filter" class="form-label visually-hidden">Filter</label>
<div class="form-group">

View File

@ -20,7 +20,7 @@
<h5 class="card-title">{{theme.name | sentenceCase}}</h5>
<h6 class="card-subtitle mb-2 text-muted">{{theme.provider | siteThemeProvider}}</h6>
<button class="btn btn-secondary me-2" [disabled]="theme.isDefault" *ngIf="isAdmin" (click)="updateDefault(theme)">Set Default</button>
<button class="btn btn-primary" (click)="applyTheme(theme)" [disabled]="currentTheme.id === theme.id">{{currentTheme.id === theme.id ? 'Applied' : 'Apply'}}</button>
<button class="btn btn-primary" (click)="applyTheme(theme)" [disabled]="currentTheme?.id === theme.id">{{currentTheme?.id === theme.id ? 'Applied' : 'Apply'}}</button>
</div>
</div>
</ng-container>

View File

@ -14,7 +14,7 @@ import { AccountService } from 'src/app/_services/account.service';
})
export class ThemeManagerComponent implements OnInit, OnDestroy {
currentTheme!: SiteTheme;
currentTheme: SiteTheme | undefined;
isAdmin: boolean = false;
user: User | undefined;