1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2024-11-03 21:04:03 +08:00

Smaller performance tweeks to thumbnail reading

#437
This commit is contained in:
Patrik J. Braun 2022-06-27 23:46:17 +02:00
parent c06620a795
commit 849ebbec9e
4 changed files with 58 additions and 43 deletions

View File

@ -1,23 +1,23 @@
import * as path from 'path'; import * as path from 'path';
import * as fs from 'fs'; import * as fs from 'fs';
import { NextFunction, Request, Response } from 'express'; import {NextFunction, Request, Response} from 'express';
import { ErrorCodes, ErrorDTO } from '../../../common/entities/Error'; import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error';
import { ContentWrapper } from '../../../common/entities/ConentWrapper'; import {ContentWrapper} from '../../../common/entities/ConentWrapper';
import { import {
ParentDirectoryDTO, ParentDirectoryDTO,
SubDirectoryDTO, SubDirectoryDTO,
} from '../../../common/entities/DirectoryDTO'; } from '../../../common/entities/DirectoryDTO';
import { ProjectPath } from '../../ProjectPath'; import {ProjectPath} from '../../ProjectPath';
import { Config } from '../../../common/config/private/Config'; import {Config} from '../../../common/config/private/Config';
import { ThumbnailSourceType } from '../../model/threading/PhotoWorker'; import {ThumbnailSourceType} from '../../model/threading/PhotoWorker';
import { MediaDTO } from '../../../common/entities/MediaDTO'; import {MediaDTO} from '../../../common/entities/MediaDTO';
import { PhotoProcessing } from '../../model/fileprocessing/PhotoProcessing'; import {PhotoProcessing} from '../../model/fileprocessing/PhotoProcessing';
import { PersonWithSampleRegion } from '../../../common/entities/PersonDTO'; import {PersonWithSampleRegion} from '../../../common/entities/PersonDTO';
import { ServerTime } from '../ServerTimingMWs'; import {ServerTime} from '../ServerTimingMWs';
export class ThumbnailGeneratorMWs { export class ThumbnailGeneratorMWs {
private static ThumbnailMap: { [key: number]: number } = private static ThumbnailMapEntries =
Config.Client.Media.Thumbnail.generateThumbnailMap(); Config.Client.Media.Thumbnail.generateThumbnailMapEntries();
@ServerTime('2.th', 'Thumbnail decoration') @ServerTime('2.th', 'Thumbnail decoration')
public static async addThumbnailInformation( public static async addThumbnailInformation(
@ -34,6 +34,10 @@ export class ThumbnailGeneratorMWs {
if (cw.notModified === true) { if (cw.notModified === true) {
return next(); return next();
} }
// regenerate in case the list change since startup
ThumbnailGeneratorMWs.ThumbnailMapEntries =
Config.Client.Media.Thumbnail.generateThumbnailMapEntries();
if (cw.directory) { if (cw.directory) {
ThumbnailGeneratorMWs.addThInfoTODir(cw.directory); ThumbnailGeneratorMWs.addThInfoTODir(cw.directory);
} }
@ -209,8 +213,6 @@ export class ThumbnailGeneratorMWs {
private static addThInfoTODir( private static addThInfoTODir(
directory: ParentDirectoryDTO | SubDirectoryDTO directory: ParentDirectoryDTO | SubDirectoryDTO
): void { ): void {
ThumbnailGeneratorMWs.ThumbnailMap =
Config.Client.Media.Thumbnail.generateThumbnailMap();
if (typeof directory.media !== 'undefined') { if (typeof directory.media !== 'undefined') {
ThumbnailGeneratorMWs.addThInfoToPhotos(directory.media); ThumbnailGeneratorMWs.addThInfoToPhotos(directory.media);
} }
@ -220,8 +222,8 @@ export class ThumbnailGeneratorMWs {
} }
private static addThInfoToPhotos(photos: MediaDTO[]): void { private static addThInfoToPhotos(photos: MediaDTO[]): void {
for (const item of photos) { for (let i = 0; i < photos.length; ++i) {
this.addThInfoToAPhoto(item); this.addThInfoToAPhoto(photos[i]);
} }
} }
@ -232,19 +234,18 @@ export class ThumbnailGeneratorMWs {
photo.directory.name, photo.directory.name,
photo.name photo.name
); );
for (const _s of Object.keys(ThumbnailGeneratorMWs.ThumbnailMap)) { for (let i = 0; i < ThumbnailGeneratorMWs.ThumbnailMapEntries.length; ++i) {
const size = parseInt(_s) const entry = ThumbnailGeneratorMWs.ThumbnailMapEntries[i];
const thPath = PhotoProcessing.generateConvertedPath( const thPath = PhotoProcessing.generateConvertedPath(
fullMediaPath, fullMediaPath,
size entry.size
); );
if (fs.existsSync(thPath) !== true) { if (fs.existsSync(thPath) !== true) {
if (typeof photo.missingThumbnails === 'undefined') { if (typeof photo.missingThumbnails === 'undefined') {
photo.missingThumbnails = 0; photo.missingThumbnails = 0;
} }
// this is a bitwise operation // this is a bitwise operation
photo.missingThumbnails += photo.missingThumbnails += entry.bit;
ThumbnailGeneratorMWs.ThumbnailMap[size];
} }
} }
} }

View File

@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/no-inferrable-types */ /* eslint-disable @typescript-eslint/no-inferrable-types */
import 'reflect-metadata'; import 'reflect-metadata';
import { SortingMethods } from '../../entities/SortingMethods'; import {SortingMethods} from '../../entities/SortingMethods';
import { UserRoles } from '../../entities/UserDTO'; import {UserRoles} from '../../entities/UserDTO';
import { ConfigProperty, SubConfigClass } from 'typeconfig/common'; import {ConfigProperty, SubConfigClass} from 'typeconfig/common';
import { IPrivateConfig } from '../private/PrivateConfig'; import {IPrivateConfig} from '../private/PrivateConfig';
export enum MapProviders { export enum MapProviders {
OpenStreetMap = 1, OpenStreetMap = 1,
@ -15,11 +15,11 @@ export enum MapProviders {
export class AutoCompleteConfig { export class AutoCompleteConfig {
@ConfigProperty() @ConfigProperty()
enabled: boolean = true; enabled: boolean = true;
@ConfigProperty({ type: 'unsignedInt' }) @ConfigProperty({type: 'unsignedInt'})
targetItemsPerCategory: number = 5; targetItemsPerCategory: number = 5;
@ConfigProperty({ type: 'unsignedInt' }) @ConfigProperty({type: 'unsignedInt'})
maxItems: number = 30; maxItems: number = 30;
@ConfigProperty({ type: 'unsignedInt' }) @ConfigProperty({type: 'unsignedInt'})
cacheTimeout: number = 1000 * 60 * 60; cacheTimeout: number = 1000 * 60 * 60;
} }
@ -27,11 +27,11 @@ export class AutoCompleteConfig {
export class ClientSearchConfig { export class ClientSearchConfig {
@ConfigProperty() @ConfigProperty()
enabled: boolean = true; enabled: boolean = true;
@ConfigProperty({ type: 'unsignedInt' }) @ConfigProperty({type: 'unsignedInt'})
searchCacheTimeout: number = 1000 * 60 * 60; searchCacheTimeout: number = 1000 * 60 * 60;
@ConfigProperty() @ConfigProperty()
AutoComplete: AutoCompleteConfig = new AutoCompleteConfig(); AutoComplete: AutoCompleteConfig = new AutoCompleteConfig();
@ConfigProperty({ type: 'unsignedInt' }) @ConfigProperty({type: 'unsignedInt'})
maxMediaResult: number = 10000; maxMediaResult: number = 10000;
@ConfigProperty({ @ConfigProperty({
description: 'Search returns also with directories, not just media', description: 'Search returns also with directories, not just media',
@ -42,7 +42,7 @@ export class ClientSearchConfig {
'Search also returns with metafiles from directories that contain a media file of the matched search result', 'Search also returns with metafiles from directories that contain a media file of the matched search result',
}) })
listMetafiles: boolean = true; listMetafiles: boolean = true;
@ConfigProperty({ type: 'unsignedInt' }) @ConfigProperty({type: 'unsignedInt'})
maxDirectoryResult: number = 200; maxDirectoryResult: number = 200;
} }
@ -62,7 +62,7 @@ export class ClientSharingConfig {
@SubConfigClass() @SubConfigClass()
export class ClientRandomPhotoConfig { export class ClientRandomPhotoConfig {
@ConfigProperty({ description: 'Enables random link generation.' }) @ConfigProperty({description: 'Enables random link generation.'})
enabled: boolean = true; enabled: boolean = true;
} }
@ -92,23 +92,23 @@ export class ClientMapConfig {
maxPreviewMarkers: number = 50; maxPreviewMarkers: number = 50;
@ConfigProperty() @ConfigProperty()
useImageMarkers: boolean = true; useImageMarkers: boolean = true;
@ConfigProperty({ type: MapProviders }) @ConfigProperty({type: MapProviders})
mapProvider: MapProviders = MapProviders.OpenStreetMap; mapProvider: MapProviders = MapProviders.OpenStreetMap;
@ConfigProperty() @ConfigProperty()
mapboxAccessToken: string = ''; mapboxAccessToken: string = '';
@ConfigProperty({ arrayType: MapLayers }) @ConfigProperty({arrayType: MapLayers})
customLayers: MapLayers[] = [new MapLayers()]; customLayers: MapLayers[] = [new MapLayers()];
} }
@SubConfigClass() @SubConfigClass()
export class ClientThumbnailConfig { export class ClientThumbnailConfig {
@ConfigProperty({ type: 'unsignedInt', max: 100 }) @ConfigProperty({type: 'unsignedInt', max: 100})
iconSize: number = 45; iconSize: number = 45;
@ConfigProperty({ type: 'unsignedInt' }) @ConfigProperty({type: 'unsignedInt'})
personThumbnailSize: number = 200; personThumbnailSize: number = 200;
@ConfigProperty({ arrayType: 'unsignedInt' }) @ConfigProperty({arrayType: 'unsignedInt'})
thumbnailSizes: number[] = [240, 480]; thumbnailSizes: number[] = [240, 480];
@ConfigProperty({ volatile: true }) @ConfigProperty({volatile: true})
concurrentThumbnailGenerations: number = 1; concurrentThumbnailGenerations: number = 1;
/** /**
@ -121,6 +121,13 @@ export class ClientThumbnailConfig {
}); });
return m; return m;
} }
/**
* Generates a map for bitwise operation from icon and normal thumbnails
*/
generateThumbnailMapEntries(): { size: number, bit: number }[] {
return Object.entries(this.generateThumbnailMap()).map(v => ({size: parseInt(v[0]), bit: v[1]}));
}
} }
@SubConfigClass() @SubConfigClass()
@ -137,7 +144,7 @@ export class ClientOtherConfig {
enableCache: boolean = true; enableCache: boolean = true;
@ConfigProperty() @ConfigProperty()
enableOnScrollRendering: boolean = true; enableOnScrollRendering: boolean = true;
@ConfigProperty({ type: SortingMethods }) @ConfigProperty({type: SortingMethods})
defaultPhotoSortingMethod: SortingMethods = SortingMethods.ascDate; defaultPhotoSortingMethod: SortingMethods = SortingMethods.ascDate;
@ConfigProperty({ @ConfigProperty({
description: description:
@ -227,9 +234,9 @@ export class ClientFacesConfig {
enabled: boolean = true; enabled: boolean = true;
@ConfigProperty() @ConfigProperty()
keywordsToPersons: boolean = true; keywordsToPersons: boolean = true;
@ConfigProperty({ type: UserRoles }) @ConfigProperty({type: UserRoles})
writeAccessMinRole: UserRoles = UserRoles.Admin; writeAccessMinRole: UserRoles = UserRoles.Admin;
@ConfigProperty({ type: UserRoles }) @ConfigProperty({type: UserRoles})
readAccessMinRole: UserRoles = UserRoles.User; readAccessMinRole: UserRoles = UserRoles.User;
} }
@ -255,9 +262,9 @@ export class ClientConfig {
Other: ClientOtherConfig = new ClientOtherConfig(); Other: ClientOtherConfig = new ClientOtherConfig();
@ConfigProperty() @ConfigProperty()
authenticationRequired: boolean = true; authenticationRequired: boolean = true;
@ConfigProperty({ type: UserRoles }) @ConfigProperty({type: UserRoles})
unAuthenticatedUserRole: UserRoles = UserRoles.Admin; unAuthenticatedUserRole: UserRoles = UserRoles.Admin;
@ConfigProperty({ arrayType: 'string', volatile: true }) @ConfigProperty({arrayType: 'string', volatile: true})
languages: string[] | undefined; languages: string[] | undefined;
@ConfigProperty() @ConfigProperty()
Media: ClientMediaConfig = new ClientMediaConfig(); Media: ClientMediaConfig = new ClientMediaConfig();

View File

@ -164,6 +164,10 @@ export class ContentWrapper {
} }
} }
if (m.missingThumbnails === 0) {
delete m.missingThumbnails;
}
if (MediaDTOUtils.isPhoto(m)) { if (MediaDTOUtils.isPhoto(m)) {
delete (m as VideoDTO).metadata.bitRate; delete (m as VideoDTO).metadata.bitRate;
delete (m as VideoDTO).metadata.duration; delete (m as VideoDTO).metadata.duration;

View File

@ -34,6 +34,9 @@ describe('ContentWrapper', () => {
delete (m as PhotoDTO).metadata.faces; delete (m as PhotoDTO).metadata.faces;
delete (m as PhotoDTO).metadata.positionData; delete (m as PhotoDTO).metadata.positionData;
} }
if (m.missingThumbnails === 0) {
delete m.missingThumbnails;
}
} }
for (let i = 0; i < content.metaFile.length; ++i) { for (let i = 0; i < content.metaFile.length; ++i) {
delete content.metaFile[i].id; delete content.metaFile[i].id;