More Fixes (#1993)

* Strip just isbn: from epub isbns and log when it's back (books)

* Tweaked to allow invalid GTINs but only valid ISBN 10/13s will be saved to Kavita.

* Fixed a bug with parsing series from a filename that is just a chapter range and no chapter/volume keywords.

* Show the media issue count before you open accordion

* Added a inpage filter for Media issues

* Cleanup styles

* Fixed up some code in epub isbn parsing when it's null

* Encode filenames when downloading so that non english characters can be passed properly to UI.

* Added support to parse ComicInfo's with Empty Tags.

* Reset development settings.

* Tweaked the code in generating reading lists to avoid extra work when not needed.

* Fix comicvine's favicon

* Fixed up a unit test

* Tweaked the favicon code to ignore icons that have query parameters

* More favicon work. Expanded ability to grab icons a bit. Added in ability to not keep requesting favicons when we failed to parse already.

* Added a note for later

* Fixed stats server url

* Added more debugging

* Fixed unit tests
This commit is contained in:
Joe Milazzo 2023-05-14 18:14:27 -05:00 committed by GitHub
parent cd8fca993b
commit 25703d6fe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 171 additions and 75 deletions

View file

@ -1,6 +1,16 @@
<p>This table contains issues found during scan or reading of your media. This list is non-managed. You can clear it at any time and use Library (Force) Scan to perform analysis.</p>
<button class="btn btn-primary mb-2" (click)="clear()">Clear Alerts</button>
<form [formGroup]="formGroup">
<div class="row g-0 mb-3">
<div class="col-md-12">
<label for="filter" class="visually-hidden">Filter</label>
<div class="input-group">
<input id="filter" type="text" class="form-control" placeholder="Filter" formControlName="filter" />
<button class="btn btn-primary" type="button" (click)="clear()">Clear Alerts</button>
</div>
</div>
</div>
</form>
<table class="table table-light table-hover table-sm table-hover">
<thead #header>
<tr>
@ -20,20 +30,22 @@
</thead>
<tbody #container>
<tr *ngIf="isLoading"><td colspan="4" style="text-align: center;"><app-loading [loading]="isLoading"></app-loading></td></tr>
<tr *ngIf="data.length === 0 && !isLoading"><td colspan="4" style="text-align: center;">No issues</td></tr>
<tr *ngFor="let item of data; index as i">
<td>
{{item.extension}}
</td>
<td>
{{item.filePath}}
</td>
<td>
{{item.comment}}
</td>
<td>
{{item.details}}
</td>
</tr>
<ng-container *ngIf="data | filter: filterList as filteredData">
<tr *ngIf="filteredData.length === 0 && !isLoading"><td colspan="4" style="text-align: center;">No issues</td></tr>
<tr *ngFor="let item of filteredData; index as i">
<td>
{{item.extension}}
</td>
<td>
{{item.filePath}}
</td>
<td>
{{item.comment}}
</td>
<td>
{{item.details}}
</td>
</tr>
</ng-container>
</tbody>
</table>

View file

@ -1,9 +1,10 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnInit, QueryList, ViewChildren, inject } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnInit, Output, QueryList, ViewChildren, inject } from '@angular/core';
import { BehaviorSubject, Observable, Subject, combineLatest, filter, map, shareReplay, takeUntil } from 'rxjs';
import { SortEvent, SortableHeader, compare } from 'src/app/_single-module/table/_directives/sortable-header.directive';
import { KavitaMediaError } from '../_models/media-error';
import { ServerService } from 'src/app/_services/server.service';
import { EVENTS, MessageHubService } from 'src/app/_services/message-hub.service';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-manage-alerts',
@ -13,6 +14,7 @@ import { EVENTS, MessageHubService } from 'src/app/_services/message-hub.service
})
export class ManageAlertsComponent implements OnInit {
@Output() alertCount = new EventEmitter<number>();
@ViewChildren(SortableHeader<KavitaMediaError>) headers!: QueryList<SortableHeader<KavitaMediaError>>;
private readonly serverService = inject(ServerService);
private readonly messageHub = inject(MessageHubService);
@ -25,6 +27,9 @@ export class ManageAlertsComponent implements OnInit {
data: Array<KavitaMediaError> = [];
isLoading = true;
formGroup = new FormGroup({
filter: new FormControl('', [])
});
constructor() {}
@ -63,8 +68,7 @@ export class ManageAlertsComponent implements OnInit {
this.serverService.getMediaErrors().subscribe(d => {
this.data = d;
this.isLoading = false;
console.log(this.data)
console.log(this.isLoading)
this.alertCount.emit(d.length);
this.cdRef.detectChanges();
});
}
@ -72,4 +76,9 @@ export class ManageAlertsComponent implements OnInit {
clear() {
this.serverService.clearMediaAlerts().subscribe(_ => this.loadData());
}
filterList = (listItem: KavitaMediaError) => {
const query = (this.formGroup.get('filter')?.value || '').toLowerCase();
return listItem.comment.toLowerCase().indexOf(query) >= 0 || listItem.filePath.toLowerCase().indexOf(query) >= 0 || listItem.details.indexOf(query) >= 0;
}
}

View file

@ -37,13 +37,13 @@
</div>
</form>
<ngb-accordion #a="ngbAccordion">
<ngb-accordion #a="ngbAccordion" [destroyOnHide]="false">
<ngb-panel>
<ng-template ngbPanelTitle>
Media Issues
Media Issues <span class="ms-1" *ngIf="alertCount > 0">({{alertCount}})</span>
</ng-template>
<ng-template ngbPanelContent>
<app-manage-alerts></app-manage-alerts>
<app-manage-alerts (alertCount)="alertCount = $event"></app-manage-alerts>
</ng-template>
</ngb-panel>
</ngb-accordion>

View file

@ -18,6 +18,8 @@ export class ManageMediaSettingsComponent implements OnInit {
serverSettings!: ServerSettings;
settingsForm: FormGroup = new FormGroup({});
alertCount: number = 0;
get EncodeFormats() { return EncodeFormats; }
constructor(private settingsService: SettingsService, private toastr: ToastrService, private modalService: NgbModal, ) { }

View file

@ -55,6 +55,7 @@ export class DownloadService {
private downloadsSource: BehaviorSubject<DownloadEvent[]> = new BehaviorSubject<DownloadEvent[]>([]);
public activeDownloads$ = this.downloadsSource.asObservable();
constructor(private httpClient: HttpClient, private confirmService: ConfirmService,
@Inject(SAVER) private save: Saver, private accountService: AccountService) { }
@ -159,7 +160,7 @@ export class DownloadService {
).pipe(
throttleTime(DEBOUNCE_TIME, asyncScheduler, { leading: true, trailing: true }),
download((blob, filename) => {
this.save(blob, filename);
this.save(blob, decodeURIComponent(filename));
}),
tap((d) => this.updateDownloadState(d, downloadType, subtitle)),
finalize(() => this.finalizeDownloadState(downloadType, subtitle))
@ -174,7 +175,7 @@ export class DownloadService {
).pipe(
throttleTime(DEBOUNCE_TIME, asyncScheduler, { leading: true, trailing: true }),
download((blob, filename) => {
this.save(blob, filename);
this.save(blob, decodeURIComponent(filename));
}),
tap((d) => this.updateDownloadState(d, downloadType, subtitle)),
finalize(() => this.finalizeDownloadState(downloadType, subtitle))
@ -213,7 +214,7 @@ export class DownloadService {
).pipe(
throttleTime(DEBOUNCE_TIME, asyncScheduler, { leading: true, trailing: true }),
download((blob, filename) => {
this.save(blob, filename);
this.save(blob, decodeURIComponent(filename));
}),
tap((d) => this.updateDownloadState(d, downloadType, subtitle)),
finalize(() => this.finalizeDownloadState(downloadType, subtitle))
@ -228,7 +229,7 @@ export class DownloadService {
).pipe(
throttleTime(DEBOUNCE_TIME, asyncScheduler, { leading: true, trailing: true }),
download((blob, filename) => {
this.save(blob, filename);
this.save(blob, decodeURIComponent(filename));
}),
tap((d) => this.updateDownloadState(d, downloadType, subtitle)),
finalize(() => this.finalizeDownloadState(downloadType, subtitle))
@ -248,7 +249,7 @@ export class DownloadService {
).pipe(
throttleTime(DEBOUNCE_TIME, asyncScheduler, { leading: true, trailing: true }),
download((blob, filename) => {
this.save(blob, filename);
this.save(blob, decodeURIComponent(filename));
}),
tap((d) => this.updateDownloadState(d, downloadType, subtitle)),
finalize(() => this.finalizeDownloadState(downloadType, subtitle))