1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2024-12-27 02:09:16 +02:00

Fixing settings bugs, adding max number of thumbnail rendering thread settings

This commit is contained in:
Patrik J. Braun 2018-11-02 16:24:37 +01:00
parent fd74f761dc
commit 90c04b7f6b
20 changed files with 196 additions and 82 deletions

View File

@ -3,13 +3,14 @@ import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
import {ObjectManagerRepository} from '../model/ObjectManagerRepository';
import {Logger} from '../Logger';
import {SQLConnection} from '../model/sql/SQLConnection';
import {DataBaseConfig, DatabaseType, IndexingConfig, ThumbnailConfig} from '../../common/config/private/IPrivateConfig';
import {DataBaseConfig, DatabaseType, IndexingConfig, IPrivateConfig, ThumbnailConfig} from '../../common/config/private/IPrivateConfig';
import {Config} from '../../common/config/private/Config';
import {ConfigDiagnostics} from '../model/ConfigDiagnostics';
import {ClientConfig} from '../../common/config/public/ConfigClass';
import {BasicConfigDTO} from '../../common/entities/settings/BasicConfigDTO';
import {OtherConfigDTO} from '../../common/entities/settings/OtherConfigDTO';
import {ProjectPath} from '../ProjectPath';
import {PrivateConfigClass} from '../../common/config/private/PrivateConfigClass';
const LOG_TAG = '[AdminMWs]';
@ -244,18 +245,21 @@ export class AdminMWs {
try {
const settings: OtherConfigDTO = req.body.settings;
Config.Client.enableCache = settings.enableCache;
Config.Client.enableOnScrollRendering = settings.enableOnScrollRendering;
Config.Client.enableOnScrollThumbnailPrioritising = settings.enableOnScrollThumbnailPrioritising;
Config.Client.defaultPhotoSortingMethod = settings.defaultPhotoSortingMethod;
Config.Client.Other.enableCache = settings.Client.enableCache;
Config.Client.Other.enableOnScrollRendering = settings.Client.enableOnScrollRendering;
Config.Client.Other.enableOnScrollThumbnailPrioritising = settings.Client.enableOnScrollThumbnailPrioritising;
Config.Client.Other.defaultPhotoSortingMethod = settings.Client.defaultPhotoSortingMethod;
Config.Client.Other.NavBar.showItemCount = settings.Client.NavBar.showItemCount;
// only updating explicitly set config (not saving config set by the diagnostics)
const original = Config.original();
original.Client.enableCache = settings.enableCache;
original.Client.enableOnScrollRendering = settings.enableOnScrollRendering;
original.Client.enableOnScrollThumbnailPrioritising = settings.enableOnScrollThumbnailPrioritising;
original.Client.defaultPhotoSortingMethod = settings.defaultPhotoSortingMethod;
original.Server.enableThreading = settings.enableThreading;
const original: PrivateConfigClass = Config.original();
original.Client.Other.enableCache = settings.Client.enableCache;
original.Client.Other.enableOnScrollRendering = settings.Client.enableOnScrollRendering;
original.Client.Other.enableOnScrollThumbnailPrioritising = settings.Client.enableOnScrollThumbnailPrioritising;
original.Client.Other.defaultPhotoSortingMethod = settings.Client.defaultPhotoSortingMethod;
original.Client.Other.NavBar.showItemCount = settings.Client.NavBar.showItemCount;
original.Server.threading.enable = settings.Server.enable;
original.Server.threading.thumbnailThreads = settings.Server.thumbnailThreads;
original.save();
await ConfigDiagnostics.runDiagnostics();
Logger.info(LOG_TAG, 'new config:');

View File

@ -26,18 +26,22 @@ export class ThumbnailGeneratorMWs {
}
if (Config.Server.enableThreading === true ||
if (Config.Server.threading.enable === true ||
Config.Server.thumbnail.processingLibrary !== ThumbnailProcessingLib.Jimp) {
Config.Client.concurrentThumbnailGenerations = Math.max(1, os.cpus().length - 1);
if (Config.Server.threading.thumbnailThreads > 0) {
Config.Client.Thumbnail.concurrentThumbnailGenerations = Config.Server.threading.thumbnailThreads;
} else {
Config.Client.Thumbnail.concurrentThumbnailGenerations = Math.max(1, os.cpus().length - 1);
}
} else {
Config.Client.concurrentThumbnailGenerations = 1;
Config.Client.Thumbnail.concurrentThumbnailGenerations = 1;
}
if (Config.Server.enableThreading === true &&
if (Config.Server.threading.enable === true &&
Config.Server.thumbnail.processingLibrary === ThumbnailProcessingLib.Jimp) {
this.taskQue = new ThumbnailTH(Config.Client.concurrentThumbnailGenerations);
this.taskQue = new ThumbnailTH(Config.Client.Thumbnail.concurrentThumbnailGenerations);
} else {
this.taskQue = new TaskQue(Config.Client.concurrentThumbnailGenerations);
this.taskQue = new TaskQue(Config.Client.Thumbnail.concurrentThumbnailGenerations);
}
this.initDone = true;

View File

@ -11,7 +11,7 @@ export class DiskManager {
static threadPool: DiskManagerTH = null;
public static init() {
if (Config.Server.enableThreading === true) {
if (Config.Server.threading.enable === true) {
DiskManager.threadPool = new DiskManagerTH(1);
}
}
@ -22,7 +22,7 @@ export class DiskManager {
let directory: DirectoryDTO = null;
if (Config.Server.enableThreading === true) {
if (Config.Server.threading.enable === true) {
directory = await DiskManager.threadPool.execute(relativeDirectoryName);
} else {
directory = await DiskMangerWorker.scanDirectory(relativeDirectoryName);

View File

@ -29,6 +29,22 @@ export class Utils {
}
/**
*
* @param from
* @param to inclusive
* @returns {Array}
*/
static createRange(from: number, to: number): Array<number> {
const arr = new Array(to - from + 1);
let c = to - from + 1;
while (c--) {
arr[c] = to--;
}
return arr;
}
static concatUrls(...args: Array<string>) {
let url = '';
for (let i = 0; i < args.length; i++) {

View File

@ -51,12 +51,17 @@ export interface IndexingConfig {
reIndexingSensitivity: ReIndexingSensitivity;
}
export interface ThreadingConfig {
enable: boolean;
thumbnailThreads: number;
}
export interface ServerConfig {
port: number;
imagesFolder: string;
thumbnail: ThumbnailConfig;
threading: ThreadingConfig;
database: DataBaseConfig;
enableThreading: boolean;
sharing: SharingConfig;
sessionTimeout: number;
indexing: IndexingConfig;

View File

@ -33,12 +33,15 @@ export class PrivateConfigClass extends PublicConfigClass implements IPrivateCon
sharing: {
updateTimeout: 1000 * 60 * 5
},
threading: {
enable: true,
thumbnailThreads: 0
},
indexing: {
folderPreviewSize: 2,
cachedFolderTimeout: 1000 * 60 * 60,
reIndexingSensitivity: ReIndexingSensitivity.medium
},
enableThreading: true
}
};
private ConfigLoader: any;

View File

@ -28,24 +28,33 @@ export module ClientConfig {
export interface ThumbnailConfig {
iconSize: number;
thumbnailSizes: Array<number>;
concurrentThumbnailGenerations: number;
}
export interface NavBarConfig {
showItemCount: boolean;
}
export interface OtherConfig {
enableCache: boolean;
enableOnScrollRendering: boolean;
defaultPhotoSortingMethod: SortingMethods;
enableOnScrollThumbnailPrioritising: boolean;
NavBar: NavBarConfig;
}
export interface Config {
applicationTitle: string;
publicUrl: string;
urlBase: string;
Thumbnail: ThumbnailConfig;
Search: SearchConfig;
Sharing: SharingConfig;
Map: MapConfig;
RandomPhoto: RandomPhotoConfig;
concurrentThumbnailGenerations: number;
enableCache: boolean;
enableOnScrollRendering: boolean;
enableOnScrollThumbnailPrioritising: boolean;
Other: OtherConfig;
authenticationRequired: boolean;
publicUrl: string;
urlBase: string;
languages: string[];
defaultPhotoSortingMethod: SortingMethods;
}
}
@ -58,6 +67,7 @@ export class PublicConfigClass {
public Client: ClientConfig.Config = {
applicationTitle: 'PiGallery 2',
Thumbnail: {
concurrentThumbnailGenerations: 1,
thumbnailSizes: [200, 400, 600],
iconSize: 30
},
@ -81,15 +91,19 @@ export class PublicConfigClass {
RandomPhoto: {
enabled: true
},
concurrentThumbnailGenerations: 1,
enableCache: true,
enableOnScrollRendering: true,
enableOnScrollThumbnailPrioritising: true,
Other: {
enableCache: true,
enableOnScrollRendering: true,
enableOnScrollThumbnailPrioritising: true,
defaultPhotoSortingMethod: SortingMethods.ascDate,
NavBar: {
showItemCount: true
}
},
authenticationRequired: true,
publicUrl: '',
urlBase: '',
languages: [],
defaultPhotoSortingMethod: SortingMethods.ascDate
languages: []
};
}

View File

@ -1,9 +1,7 @@
import {SortingMethods} from '../SortingMethods';
import {ClientConfig} from '../../config/public/ConfigClass';
import {ThreadingConfig} from '../../config/private/IPrivateConfig';
export interface OtherConfigDTO {
enableCache: boolean;
enableOnScrollRendering: boolean;
enableOnScrollThumbnailPrioritising: boolean;
enableThreading: boolean;
defaultPhotoSortingMethod: SortingMethods;
Server: ThreadingConfig;
Client: ClientConfig.OtherConfig;
}

View File

@ -97,7 +97,7 @@ export class GalleryCacheService {
public getDirectory(directoryName: string): DirectoryDTO {
if (Config.Client.enableCache === false) {
if (Config.Client.Other.enableCache === false) {
return null;
}
const value = localStorage.getItem(GalleryCacheService.CONTENT_PREFIX + Utils.concatUrls(directoryName));
@ -111,7 +111,7 @@ export class GalleryCacheService {
}
public setDirectory(directory: DirectoryDTO): void {
if (Config.Client.enableCache === false) {
if (Config.Client.Other.enableCache === false) {
return;
}
@ -137,7 +137,7 @@ export class GalleryCacheService {
*/
public photoUpdated(photo: PhotoDTO): void {
if (Config.Client.enableCache === false) {
if (Config.Client.Other.enableCache === false) {
return;
}

View File

@ -26,7 +26,7 @@ export class GalleryService {
private _shareService: ShareService,
private navigatoinService: NavigationService) {
this.content = new BehaviorSubject<ContentWrapper>(new ContentWrapper());
this.sorting = new BehaviorSubject<SortingMethods>(Config.Client.defaultPhotoSortingMethod);
this.sorting = new BehaviorSubject<SortingMethods>(Config.Client.Other.defaultPhotoSortingMethod);
}
lastRequest: { directory: string } = {

View File

@ -137,7 +137,7 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
ngAfterViewInit() {
this.lightbox.setGridPhotoQL(this.gridPhotoQL);
if (Config.Client.enableOnScrollThumbnailPrioritising === true) {
if (Config.Client.Other.enableOnScrollThumbnailPrioritising === true) {
this.gridPhotoQL.changes.subscribe(() => {
this.scrollListenerPhotos = this.gridPhotoQL.filter(pc => pc.ScrollListener);
});
@ -273,7 +273,7 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
* @returns {boolean}
*/
private shouldRenderMore(offset: number = 0): boolean {
return Config.Client.enableOnScrollRendering === false ||
return Config.Client.Other.enableOnScrollRendering === false ||
PageHelper.ScrollY >= (document.body.clientHeight + offset - window.innerHeight) * 0.7
|| (document.body.clientHeight + offset) * 0.85 < window.innerHeight;
@ -288,7 +288,7 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
window.requestAnimationFrame(() => {
this.renderPhotos();
if (Config.Client.enableOnScrollThumbnailPrioritising === true) {
if (Config.Client.Other.enableOnScrollThumbnailPrioritising === true) {
this.scrollListenerPhotos.forEach((pc: GalleryPhotoComponent) => {
pc.onScroll();
});

View File

@ -20,7 +20,7 @@
}
ol {
padding-right: 130px;
padding-right: 150px;
}
.dropdown-menu {

View File

@ -8,10 +8,10 @@
</ol>
<div class="right-side">
<div class="photos-count" *ngIf="directory.photos.length > 0">
<div class="photos-count" *ngIf="directory.photos.length > 0 && config.Client.Other.NavBar.showItemCount">
{{directory.photos.length}} <span i18n>items</span>
</div>
<div class="divider" *ngIf="directory.photos.length > 0">&nbsp;</div>
<div class="divider" *ngIf="directory.photos.length > 0 && config.Client.Other.NavBar.showItemCount">&nbsp;</div>
<div class="btn-group" dropdown placement="bottom right">
<button id="button-alignment" dropdownToggle type="button"
class="btn btn-default dropdown-toggle" aria-controls="dropdown-alignment"

View File

@ -8,6 +8,7 @@ import {QueryService} from '../../model/query.service';
import {GalleryService} from '../gallery.service';
import {Utils} from '../../../../common/Utils';
import {SortingMethods} from '../../../../common/entities/SortingMethods';
import {Config} from '../../../../common/config/public/Config';
@Component({
selector: 'app-gallery-navbar',
@ -21,6 +22,7 @@ export class GalleryNavigatorComponent implements OnChanges {
routes: Array<NavigatorPath> = [];
SortingMethods = SortingMethods;
sortingMethodsType: { key: number; value: string }[] = [];
config = Config;
constructor(private _authService: AuthenticationService,
public queryService: QueryService,

View File

@ -19,7 +19,7 @@ export class ThumbnailLoaderService {
}
run = () => {
if (this.que.length === 0 || this.runningRequests >= Config.Client.concurrentThumbnailGenerations) {
if (this.que.length === 0 || this.runningRequests >= Config.Client.Thumbnail.concurrentThumbnailGenerations) {
return;
}
const task = this.getNextTask();

View File

@ -19,7 +19,7 @@ export abstract class SettingsComponent<T, S extends AbstractSettingsService<T>
@ViewChild('settingsForm')
form: HTMLFormElement;
@Output('hasAvailableSettings')
@Output()
hasAvailableSettings = true;
public inProgress = false;
@ -61,6 +61,31 @@ export abstract class SettingsComponent<T, S extends AbstractSettingsService<T>
this.ngOnChanges();
};
settingsSame(newSettings: T, original: T): boolean {
if (typeof original !== 'object' || original == null) {
return newSettings === original;
}
if (!newSettings) {
return false;
}
const keys = Object.keys(newSettings);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (typeof original[key] === 'undefined') {
throw new Error('unknown settings: ' + key);
}
if (typeof original[key] === 'object') {
if (this.settingsSame(newSettings[key], original[key]) === false) {
return false;
}
} else if (newSettings[key] !== original[key]) {
return false;
}
}
return true;
}
ngOnInit() {
if (!this._authService.isAuthenticated() ||
this._authService.user.value.role < UserRoles.Admin) {
@ -69,11 +94,11 @@ export abstract class SettingsComponent<T, S extends AbstractSettingsService<T>
}
this.getSettings();
this._subscription = this.form.valueChanges.subscribe((data) => {
if (!data) {
return;
}
this.changed = !Utils.equalsFilter(data, this.original);
// TODO: fix after this issue is fixed: https://github.com/angular/angular/issues/24818
this._subscription = this.form.valueChanges.subscribe(() => {
setTimeout(() => {
this.changed = !this.settingsSame(this.settings, this.original);
}, 0);
});
}

View File

@ -31,12 +31,12 @@
</div>
<div class="form-group row">
<label class="col-md-2 control-label" for="folder" i18n>Images folder</label>
<label class="col-md-2 control-label" for="imagesFolder" i18n>Images folder</label>
<div class="col-md-10">
<input type="text" class="form-control" placeholder="path"
id="folder"
id="imagesFolder"
[(ngModel)]="settings.imagesFolder"
name="folder" required>
name="imagesFolder" required>
<small class="form-text text-muted" i18n>Images are loaded from this folder (read permission required)</small>
</div>
</div>

View File

@ -1,4 +1,4 @@
<form #settingsForm="ngForm" class="form-horizontal">
<form #settingsForm="ngForm" class="form-horizontal" >
<div class="card mb-4">
<h5 class="card-header" i18n>
Other settings
@ -7,20 +7,21 @@
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong i18n>Error: </strong>{{error}}</div>
<div class="form-group row">
<p class="title" i18n>Threads:</p>
<div class="form-group row" >
<label class="col-md-2 control-label" for="enableThreading" i18n>Threading</label>
<div class="col-md-10">
<bSwitch
id="enableThreading"
class="switch"
name="enableThreading"
name="enable"
[switch-on-color]="'primary'"
[switch-inverse]="'inverse'"
[switch-off-text]="text.Disabled"
[switch-on-text]="text.Enabled"
[switch-handle-width]="'100'"
[switch-label-width]="'20'"
[(ngModel)]="settings.enableThreading">
[(ngModel)]="settings.Server.enable">
</bSwitch>
<small class="form-text text-muted" i18n>Runs directory scanning and thumbnail generation (only for Jimp) in a
different thread
@ -28,6 +29,20 @@
</div>
</div>
<div class="form-group row" [hidden]="simplifiedMode || settings.Server.enable == false">
<label class="col-md-2 control-label" for="thumbnailThreads" i18n>Thumbnail threads</label>
<div class="col-md-10">
<select id="thumbnailThreads" class="form-control" [(ngModel)]="settings.Server.thumbnailThreads"
name="Server[thumbnailThreads]" required>
<option [ngValue]="0">auto</option>
<option *ngFor="let i of threads" [ngValue]="i">{{i}}</option>
</select>
<small class="form-text text-muted" i18n>Number of threads that are used to generate thumbnails. If auto, number of CPU cores -1 threads will be used.</small>
</div>
</div>
<p class="title" i18n>Misc:</p>
<div class="form-group row">
<label class="col-md-2 control-label" for="enableOnScrollThumbnailPrioritising" i18n>Scroll based thumbnail
generation</label>
@ -42,7 +57,7 @@
[switch-on-text]="text.Enabled"
[switch-handle-width]="'100'"
[switch-label-width]="'20'"
[(ngModel)]="settings.enableOnScrollThumbnailPrioritising">
[(ngModel)]="settings.Client.enableOnScrollThumbnailPrioritising">
</bSwitch>
<small class="form-text text-muted" i18n>Those thumbnails get higher priority that are visible on the screen
</small>
@ -62,7 +77,7 @@
[switch-on-text]="text.Enabled"
[switch-handle-width]="'100'"
[switch-label-width]="'20'"
[(ngModel)]="settings.enableOnScrollRendering">
[(ngModel)]="settings.Client.enableOnScrollRendering">
</bSwitch>
<small class="form-text text-muted" i18n>Shows only the required amount of photos at once. Renders more if
page bottom is reached
@ -84,7 +99,7 @@
[switch-on-text]="text.Enabled"
[switch-handle-width]="'100'"
[switch-label-width]="'20'"
[(ngModel)]="settings.enableCache">
[(ngModel)]="settings.Client.enableCache">
</bSwitch>
<small class="form-text text-muted" i18n>Caches directory contents and search results for better performance
</small>
@ -92,10 +107,33 @@
</div>
<p class="title" i18n>Navigation bar:</p>
<div class="form-group row">
<label class="col-md-2 control-label" for="showItemCount" [hidden]="simplifiedMode" i18n>Show item count</label>
<div class="col-md-10">
<bSwitch
id="showItemCount"
class="switch"
name="enableCache"
[switch-on-color]="'primary'"
[switch-inverse]="'inverse'"
[switch-off-text]="text.Disabled"
[switch-on-text]="text.Enabled"
[switch-handle-width]="'100'"
[switch-label-width]="'20'"
[(ngModel)]="settings.Client.NavBar.showItemCount">
</bSwitch>
<small class="form-text text-muted" i18n>Show hte number of items (photos) in the folder
</small>
</div>
</div>
<div class="form-group row" [hidden]="simplifiedMode">
<label class="col-md-2 control-label" for="defaultPhotoSortingMethod" i18n>Default photo sorting method</label>
<div class="col-md-10">
<select id="defaultPhotoSortingMethod" class="form-control" [(ngModel)]="settings.defaultPhotoSortingMethod"
<select id="defaultPhotoSortingMethod" class="form-control" [(ngModel)]="settings.Client.defaultPhotoSortingMethod"
name="defaultPhotoSortingMethod" required>
<option *ngFor="let type of types" [ngValue]="type.key">{{type.key | stringifySorting}}
</option>

View File

@ -6,7 +6,6 @@ import {NotificationService} from '../../model/notification.service';
import {OtherSettingsService} from './other.settings.service';
import {OtherConfigDTO} from '../../../../common/entities/settings/OtherConfigDTO';
import {I18n} from '@ngx-translate/i18n-polyfill';
import {ReIndexingSensitivity} from '../../../../common/config/private/IPrivateConfig';
import {Utils} from '../../../../common/Utils';
import {SortingMethods} from '../../../../common/entities/SortingMethods';
@ -21,6 +20,7 @@ export class OtherSettingsComponent extends SettingsComponent<OtherConfigDTO> im
types: { key: number; value: string }[] = [];
threads: number[] = Utils.createRange(1, 100);
constructor(_authService: AuthenticationService,
_navigation: NavigationService,
@ -28,11 +28,8 @@ export class OtherSettingsComponent extends SettingsComponent<OtherConfigDTO> im
notification: NotificationService,
i18n: I18n) {
super(i18n('Other'), _authService, _navigation, _settingsService, notification, i18n, s => ({
enableThreading: s.Server.enableThreading,
enableOnScrollThumbnailPrioritising: s.Client.enableOnScrollThumbnailPrioritising,
enableOnScrollRendering: s.Client.enableOnScrollRendering,
enableCache: s.Client.enableCache,
defaultPhotoSortingMethod: s.Client.defaultPhotoSortingMethod
Server: s.Server.threading,
Client: s.Client.Other
}));
this.types = Utils.enumToArray(SortingMethods);
this.hasAvailableSettings = !this.simplifiedMode;

View File

@ -20,8 +20,8 @@ export class SettingsService {
instantSearchCacheTimeout: 1000 * 60 * 60,
autocompleteCacheTimeout: 1000 * 60 * 60
},
concurrentThumbnailGenerations: null,
Thumbnail: {
concurrentThumbnailGenerations: null,
iconSize: 30,
thumbnailSizes: []
},
@ -36,15 +36,20 @@ export class SettingsService {
RandomPhoto: {
enabled: true
},
Other: {
enableCache: true,
enableOnScrollRendering: true,
enableOnScrollThumbnailPrioritising: true,
defaultPhotoSortingMethod: SortingMethods.ascDate,
NavBar: {
showItemCount: true
}
},
urlBase: '',
publicUrl: '',
applicationTitle: '',
enableCache: true,
enableOnScrollRendering: true,
enableOnScrollThumbnailPrioritising: true,
authenticationRequired: true,
languages: [],
defaultPhotoSortingMethod: SortingMethods.ascDate
languages: []
},
Server: {
database: {
@ -54,13 +59,16 @@ export class SettingsService {
updateTimeout: 2000
},
imagesFolder: '',
enableThreading: true,
port: 80,
thumbnail: {
folder: '',
qualityPriority: true,
processingLibrary: ThumbnailProcessingLib.sharp
},
threading: {
enable: true,
thumbnailThreads: 0
},
sessionTimeout: 0,
indexing: {
cachedFolderTimeout: 0,