Feature/performance pdf (#426)
# Added - Added: Added series format information to the search typeahead to help identify duplicate series in libraries # Fixed - Fixed: Fixed accent color not looking well on light theme - Fixed: Attempted to fix the memory issues with PDF reading on Docker. Uses a Memory Pool for streams and removes a bitmap operation for fixing books with transparent backgrounds (#424) # Changed - Changed: Refactored download logs to use the same download code as rest of Kavita # Dev stuff - Added timeout for Regex's to make sure during matching, malicious filenames doesn't crash user system - Refactored a missing GetCoverImage to use Series Format rather than old Library Type ================================================== * Added Timeout for Regex matching to ensure malicious filenames don't crash system * Refactored GetCoverImage to use series format rather than library type * Refactored download logs to use the download service * Fixed accent color not looking well on light theme * Refactored series format into dedicated component and added to search results * Switch to using MemoryManager for Streams to attempt to minimize GC pressure and reduced bitmap manipulation for transparency hack.
This commit is contained in:
parent
78ad01f5ae
commit
81dfd63250
21 changed files with 267 additions and 136 deletions
|
|
@ -1,3 +1,5 @@
|
|||
import { MangaFormat } from "./manga-format";
|
||||
|
||||
export interface SearchResult {
|
||||
seriesId: number;
|
||||
libraryId: number;
|
||||
|
|
@ -6,4 +8,5 @@ export interface SearchResult {
|
|||
originalName: string;
|
||||
sortName: string;
|
||||
coverImage: string; // byte64 encoded
|
||||
format: MangaFormat;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ export class ServerService {
|
|||
return this.httpClient.post(this.baseUrl + 'server/restart', {});
|
||||
}
|
||||
|
||||
fetchLogs() {
|
||||
return this.httpClient.get(this.baseUrl + 'server/logs', {responseType: 'blob' as 'text'});
|
||||
}
|
||||
|
||||
getServerInfo() {
|
||||
return this.httpClient.get<ServerInfo>(this.baseUrl + 'server/server-info');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { ToastrService } from 'ngx-toastr';
|
|||
import { ServerService } from 'src/app/_services/server.service';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { DownloadService } from 'src/app/shared/_services/download.service';
|
||||
|
||||
|
||||
|
||||
|
|
@ -23,7 +24,8 @@ export class DashboardComponent implements OnInit {
|
|||
counter = this.tabs.length + 1;
|
||||
active = this.tabs[0];
|
||||
|
||||
constructor(public route: ActivatedRoute, private serverService: ServerService, private toastr: ToastrService, private titleService: Title) {
|
||||
constructor(public route: ActivatedRoute, private serverService: ServerService,
|
||||
private toastr: ToastrService, private titleService: Title, private downloadService: DownloadService) {
|
||||
this.route.fragment.subscribe(frag => {
|
||||
const tab = this.tabs.filter(item => item.fragment === frag);
|
||||
if (tab.length > 0) {
|
||||
|
|
@ -46,10 +48,7 @@ export class DashboardComponent implements OnInit {
|
|||
}
|
||||
|
||||
fetchLogs() {
|
||||
this.serverService.fetchLogs().subscribe(res => {
|
||||
const blob = new Blob([res], {type: 'text/plain;charset=utf-8'});
|
||||
saveAs(blob, 'kavita.zip');
|
||||
});
|
||||
this.downloadService.downloadLogs();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
@import '../../../assets/themes/dark.scss';
|
||||
|
||||
.accent {
|
||||
font-style: italic;
|
||||
font-size: 0.7rem;
|
||||
background-color: $dark-form-background;
|
||||
background-color: lightgray;
|
||||
padding: 10px;
|
||||
color: lightgray;
|
||||
color: black;
|
||||
border-radius: 6px;
|
||||
box-shadow: inset 0px 0px 8px 1px $dark-form-background
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
<img class="mr-3 search-result" src="{{imageService.getSeriesCoverImage(item.seriesId)}}">
|
||||
</div>
|
||||
<div class="ml-1">
|
||||
<app-series-format [format]="item.format"></app-series-format>
|
||||
<span *ngIf="item.name.toLowerCase().indexOf(searchTerm) >= 0; else localizedName" [innerHTML]="item.name"></span>
|
||||
<ng-template #localizedName>
|
||||
<span [innerHTML]="item.localizedName"></span>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Component, HostListener, Inject, OnDestroy, OnInit, ViewChild } from '@
|
|||
import { Router } from '@angular/router';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { UtilityService } from '../shared/_services/utility.service';
|
||||
import { SearchResult } from '../_models/search-result';
|
||||
import { AccountService } from '../_services/account.service';
|
||||
import { ImageService } from '../_services/image.service';
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@
|
|||
<h5>Type</h5>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<app-tag-badge><i class="fa {{utilityService.mangaFormatIcon(series.format)}}" aria-hidden="true" title="{{utilityService.mangaFormat(series.format)}}"></i> {{utilityService.mangaFormat(series.format)}} </app-tag-badge>
|
||||
<app-tag-badge><app-series-format [format]="series.format">{{utilityService.mangaFormat(series.format)}}</app-series-format></app-tag-badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
|
||||
import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Series } from 'src/app/_models/series';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
|
@ -46,14 +46,18 @@ export class DownloadService {
|
|||
return this.httpClient.get(this.baseUrl + 'download/chapter?chapterId=' + chapterId, {observe: 'response', responseType: 'blob' as 'text'});
|
||||
}
|
||||
|
||||
downloadLogs() {
|
||||
this.httpClient.get(this.baseUrl + 'server/logs', {observe: 'response', responseType: 'blob' as 'text'}).subscribe(resp => {
|
||||
this.preformSave(resp.body || '', this.getFilenameFromHeader(resp.headers, 'logs'));
|
||||
});
|
||||
}
|
||||
|
||||
downloadSeries(series: Series) {
|
||||
this.downloadSeriesSize(series.id).subscribe(async size => {
|
||||
if (size >= this.SIZE_WARNING && !await this.confirmService.confirm('The series is ' + this.humanFileSize(size) + '. Are you sure you want to continue?')) {
|
||||
return;
|
||||
}
|
||||
this.downloadSeriesAPI(series.id).subscribe(resp => {
|
||||
//const filename = series.name + '.zip';
|
||||
//this.preformSave(res, filename);
|
||||
this.preformSave(resp.body || '', this.getFilenameFromHeader(resp.headers, series.name));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<ng-container *ngIf="format != MangaFormat.UNKNOWN">
|
||||
<i class="fa {{utilityService.mangaFormatIcon(format)}}" aria-hidden="true" title="{{utilityService.mangaFormat(format)}}"></i>
|
||||
<ng-content></ng-content>
|
||||
</ng-container>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||
import { UtilityService } from '../_services/utility.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-series-format',
|
||||
templateUrl: './series-format.component.html',
|
||||
styleUrls: ['./series-format.component.scss']
|
||||
})
|
||||
export class SeriesFormatComponent implements OnInit {
|
||||
|
||||
@Input() format: MangaFormat = MangaFormat.UNKNOWN;
|
||||
|
||||
get MangaFormat(): typeof MangaFormat {
|
||||
return MangaFormat;
|
||||
}
|
||||
|
||||
constructor(public utilityService: UtilityService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import { TagBadgeComponent } from './tag-badge/tag-badge.component';
|
|||
import { CardDetailLayoutComponent } from './card-detail-layout/card-detail-layout.component';
|
||||
import { ShowIfScrollbarDirective } from './show-if-scrollbar.directive';
|
||||
import { A11yClickDirective } from './a11y-click.directive';
|
||||
import { SeriesFormatComponent } from './series-format/series-format.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
|
|
@ -31,7 +32,8 @@ import { A11yClickDirective } from './a11y-click.directive';
|
|||
TagBadgeComponent,
|
||||
CardDetailLayoutComponent,
|
||||
ShowIfScrollbarDirective,
|
||||
A11yClickDirective
|
||||
A11yClickDirective,
|
||||
SeriesFormatComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
|
@ -54,7 +56,8 @@ import { A11yClickDirective } from './a11y-click.directive';
|
|||
TagBadgeComponent,
|
||||
CardDetailLayoutComponent,
|
||||
ShowIfScrollbarDirective,
|
||||
A11yClickDirective
|
||||
A11yClickDirective,
|
||||
SeriesFormatComponent
|
||||
]
|
||||
})
|
||||
export class SharedModule { }
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ $dark-item-accent-bg: #292d32;
|
|||
color: #4ac694;
|
||||
}
|
||||
|
||||
.accent {
|
||||
background-color: $dark-form-background !important;
|
||||
color: lightgray !important;
|
||||
box-shadow: inset 0px 0px 8px 1px $dark-form-background !important;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
background-color: $dark-item-accent-bg;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue