1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2025-01-14 14:43:17 +08:00

adding graphicsmagick support for thumbnail

This commit is contained in:
Braun Patrik 2017-06-11 23:33:47 +02:00
parent b7c8570249
commit b336a91fe0
11 changed files with 400 additions and 293 deletions

View File

@ -61,7 +61,7 @@ To configure it. Run `PiGallery2` first to create `config.json` file, then edit
* prioritizes thumbnail generation (generating thumbnail first for the visible photos) * prioritizes thumbnail generation (generating thumbnail first for the visible photos)
* saving generated thumbnails to TEMP folder for reuse * saving generated thumbnails to TEMP folder for reuse
* supporting several core CPU * supporting several core CPU
* supporting hardware acceleration ([sharp](https://github.com/lovell/sharp) as optional and JS-based [Jimp](https://github.com/oliver-moran/jimp) as fallback) * supporting hardware acceleration ([sharp](https://github.com/lovell/sharp) and [gm](https://github.com/aheckmann/gm) as optional and JS-based [Jimp](https://github.com/oliver-moran/jimp) as fallback)
* Custom lightbox for full screen photo viewing * Custom lightbox for full screen photo viewing
* keyboard support for navigation - `In progress` * keyboard support for navigation - `In progress`
* showing low-res thumbnail while full image loads * showing low-res thumbnail while full image loads

View File

@ -1,22 +1,67 @@
import {Metadata, SharpInstance} from "@types/sharp"; import {Metadata, SharpInstance} from "@types/sharp";
import {Dimensions, State} from "@types/gm";
export interface RendererInput { export module ThumbnailRenderers {
export interface RendererInput {
imagePath: string; imagePath: string;
size: number; size: number;
makeSquare: boolean; makeSquare: boolean;
thPath: string; thPath: string;
qualityPriority: boolean, qualityPriority: boolean,
__dirname: string; __dirname: string;
} }
export const softwareRenderer = (input: RendererInput, done) => { export const jimp = (input: RendererInput, done) => {
//generate thumbnail //generate thumbnail
const Jimp = require("jimp"); const Jimp = require("jimp");
Jimp.read(input.imagePath).then((image) => { Jimp.read(input.imagePath).then((image) => {
const Logger = require(input.__dirname + "/../../Logger").Logger;
Logger.silly("[JimpThRenderer] rendering thumbnail:", input.imagePath);
/**
* newWidth * newHeight = size*size
* newHeight/newWidth = height/width
*
* newHeight = (height/width)*newWidth
* newWidth * newWidth = (size*size) / (height/width)
*
* @type {number}
*/
const ratio = image.bitmap.height / image.bitmap.width;
const algo = input.qualityPriority == true ? Jimp.RESIZE_BEZIER : Jimp.RESIZE_NEAREST_NEIGHBOR;
if (input.makeSquare == false) {
let newWidth = Math.sqrt((input.size * input.size) / ratio);
image.resize(newWidth, Jimp.AUTO, algo);
} else {
image.resize(input.size / Math.min(ratio, 1), Jimp.AUTO, algo);
image.crop(0, 0, input.size, input.size);
}
image.quality(60); // set JPEG quality
image.write(input.thPath, () => { // save
return done();
});
}).catch(function (err) {
const Error = require(input.__dirname + "/../../../common/entities/Error").Error;
const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes;
return done(new Error(ErrorCodes.GENERAL_ERROR, err));
});
};
export const sharp = (input: RendererInput, done) => {
//generate thumbnail
const sharp = require("sharp");
const image: SharpInstance = sharp(input.imagePath);
image
.metadata()
.then((metadata: Metadata) => {
const Logger = require(input.__dirname + "/../../Logger").Logger; const Logger = require(input.__dirname + "/../../Logger").Logger;
Logger.silly("[SoftwareThRenderer] rendering thumbnail:", input.imagePath); Logger.silly("[SharpThRenderer] rendering thumbnail:", input.imagePath);
/** /**
* newWidth * newHeight = size*size * newWidth * newHeight = size*size
* newHeight/newWidth = height/width * newHeight/newWidth = height/width
@ -26,81 +71,99 @@ export const softwareRenderer = (input: RendererInput, done) => {
* *
* @type {number} * @type {number}
*/ */
const ratio = image.bitmap.height / image.bitmap.width; try {
const algo = input.qualityPriority == true ? Jimp.RESIZE_BEZIER : Jimp.RESIZE_NEAREST_NEIGHBOR; const ratio = metadata.height / metadata.width;
if (input.makeSquare == false) { const kernel = input.qualityPriority == true ? sharp.kernel.lanczos3 : sharp.kernel.nearest;
let newWidth = Math.sqrt((input.size * input.size) / ratio); const interpolator = input.qualityPriority == true ? sharp.interpolator.bicubic : sharp.interpolator.nearest;
if (input.makeSquare == false) {
const newWidth = Math.round(Math.sqrt((input.size * input.size) / ratio));
image.resize(newWidth, null, {
kernel: kernel,
interpolator: interpolator
});
image.resize(newWidth, Jimp.AUTO, algo); } else {
} else { image
image.resize(input.size / Math.min(ratio, 1), Jimp.AUTO, algo); .resize(input.size, input.size, {
image.crop(0, 0, input.size, input.size); kernel: kernel,
} interpolator: interpolator
image.quality(60); // set JPEG quality })
image.write(input.thPath, () => { // save .crop(sharp.strategy.center);
}
image
.jpeg()
.toFile(input.thPath).then(() => {
return done(); return done();
}); }).catch(function (err) {
}).catch(function (err) { const Error = require(input.__dirname + "/../../../common/entities/Error").Error;
const Error = require(input.__dirname + "/../../../common/entities/Error").Error; const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes;
const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes; return done(new Error(ErrorCodes.GENERAL_ERROR, err));
return done(new Error(ErrorCodes.GENERAL_ERROR, err)); });
}); } catch (err) {
}; const Error = require(input.__dirname + "/../../../common/entities/Error").Error;
const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes;
return done(new Error(ErrorCodes.GENERAL_ERROR, err));
}
});
export const hardwareRenderer = (input: RendererInput, done) => { };
export const gm = (input: RendererInput, done) => {
//generate thumbnail //generate thumbnail
const sharp = require("sharp"); const gm = require("gm");
const image: SharpInstance = sharp(input.imagePath); let image: State = gm(input.imagePath);
image image
.metadata() .size((err, value: Dimensions) => {
.then((metadata: Metadata) => { if (err) {
const Error = require(input.__dirname + "/../../../common/entities/Error").Error;
const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes;
return done(new Error(ErrorCodes.GENERAL_ERROR, err));
}
const Logger = require(input.__dirname + "/../../Logger").Logger;
Logger.silly("[GMThRenderer] rendering thumbnail:", input.imagePath);
const Logger = require(input.__dirname + "/../../Logger").Logger;
Logger.silly("[SoftwareThRenderer] rendering thumbnail:", input.imagePath);
/**
* newWidth * newHeight = size*size
* newHeight/newWidth = height/width
*
* newHeight = (height/width)*newWidth
* newWidth * newWidth = (size*size) / (height/width)
*
* @type {number}
*/
try {
const ratio = metadata.height / metadata.width;
const kernel = input.qualityPriority == true ? sharp.kernel.lanczos3 : sharp.kernel.nearest;
const interpolator = input.qualityPriority == true ? sharp.interpolator.bicubic : sharp.interpolator.nearest;
if (input.makeSquare == false) {
const newWidth = Math.round(Math.sqrt((input.size * input.size) / ratio));
image.resize(newWidth, null, {
kernel: kernel,
interpolator: interpolator
});
} else { /**
image * newWidth * newHeight = size*size
.resize(input.size, input.size, { * newHeight/newWidth = height/width
kernel: kernel, *
interpolator: interpolator * newHeight = (height/width)*newWidth
}) * newWidth * newWidth = (size*size) / (height/width)
.crop(sharp.strategy.center); *
} * @type {number}
image */
.jpeg() try {
.toFile(input.thPath).then(() => { const ratio = value.height / value.width;
return done(); const filter = input.qualityPriority == true ? 'Lanczos' : 'Point';
}).catch(function (err) { image.filter(filter);
const Error = require(input.__dirname + "/../../../common/entities/Error").Error;
const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes; if (input.makeSquare == false) {
return done(new Error(ErrorCodes.GENERAL_ERROR, err)); const newWidth = Math.round(Math.sqrt((input.size * input.size) / ratio));
}); image = image.resize(newWidth);
} catch (err) { } else {
const Error = require(input.__dirname + "/../../../common/entities/Error").Error; image = image.resize(input.size, input.size)
const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes; .crop(input.size, input.size);
return done(new Error(ErrorCodes.GENERAL_ERROR, err)); }
image.write(input.thPath, (err) => {
if (err) {
const Error = require(input.__dirname + "/../../../common/entities/Error").Error;
const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes;
return done(new Error(ErrorCodes.GENERAL_ERROR, err));
} }
}); return done();
});
} catch (err) {
const Error = require(input.__dirname + "/../../../common/entities/Error").Error;
const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes;
return done(new Error(ErrorCodes.GENERAL_ERROR, err));
}
}; });
};
}

View File

@ -9,8 +9,10 @@ import {ContentWrapper} from "../../../common/entities/ConentWrapper";
import {DirectoryDTO} from "../../../common/entities/DirectoryDTO"; import {DirectoryDTO} from "../../../common/entities/DirectoryDTO";
import {ProjectPath} from "../../ProjectPath"; import {ProjectPath} from "../../ProjectPath";
import {PhotoDTO} from "../../../common/entities/PhotoDTO"; import {PhotoDTO} from "../../../common/entities/PhotoDTO";
import {hardwareRenderer, RendererInput, softwareRenderer} from "./THRenderers"; import {ThumbnailRenderers} from "./THRenderers";
import {Config} from "../../../common/config/private/Config"; import {Config} from "../../../common/config/private/Config";
import {ThumbnailProcessingLib} from "../../../common/config/private/IPrivateConfig";
import RendererInput = ThumbnailRenderers.RendererInput;
Config.Client.concurrentThumbnailGenerations = Math.max(1, os.cpus().length - 1); Config.Client.concurrentThumbnailGenerations = Math.max(1, os.cpus().length - 1);
@ -20,157 +22,165 @@ const pool = new Pool(Config.Client.concurrentThumbnailGenerations);
export class ThumbnailGeneratorMWs { export class ThumbnailGeneratorMWs {
private static poolsInited = false; private static poolsInited = false;
private static ThumbnailFunction = null
private static initPools() { private static initPools() {
if (this.poolsInited == true) { if (this.poolsInited == true) {
return return
} }
if (Config.Server.thumbnail.hardwareAcceleration == true) { switch (Config.Server.thumbnail.processingLibrary) {
pool.run(hardwareRenderer); case ThumbnailProcessingLib.Jimp:
} else { this.ThumbnailFunction = ThumbnailRenderers.jimp;
pool.run(softwareRenderer); break;
} case ThumbnailProcessingLib.gm:
this.poolsInited = true; this.ThumbnailFunction = ThumbnailRenderers.gm;
break;
case ThumbnailProcessingLib.sharp:
this.ThumbnailFunction = ThumbnailRenderers.sharp;
break;
default:
throw "Unknown thumbnail processing lib";
}
pool.run(this.ThumbnailFunction);
this.poolsInited = true;
}
private static addThInfoTODir(directory: DirectoryDTO) {
if (typeof directory.photos == "undefined") {
directory.photos = [];
}
if (typeof directory.directories == "undefined") {
directory.directories = [];
}
ThumbnailGeneratorMWs.addThInfoToPhotos(directory.photos);
for (let i = 0; i < directory.directories.length; i++) {
ThumbnailGeneratorMWs.addThInfoTODir(directory.directories[i]);
} }
private static addThInfoTODir(directory: DirectoryDTO) { }
if (typeof directory.photos == "undefined") {
directory.photos = [];
}
if (typeof directory.directories == "undefined") {
directory.directories = [];
}
ThumbnailGeneratorMWs.addThInfoToPhotos(directory.photos);
for (let i = 0; i < directory.directories.length; i++) { private static addThInfoToPhotos(photos: Array<PhotoDTO>) {
ThumbnailGeneratorMWs.addThInfoTODir(directory.directories[i]); let thumbnailFolder = ProjectPath.ThumbnailFolder;
} for (let i = 0; i < photos.length; i++) {
let fullImagePath = path.join(ProjectPath.ImageFolder, photos[i].directory.path, photos[i].directory.name, photos[i].name);
} for (let j = 0; j < Config.Client.thumbnailSizes.length; j++) {
let size = Config.Client.thumbnailSizes[j];
private static addThInfoToPhotos(photos: Array<PhotoDTO>) { let thPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, size));
let thumbnailFolder = ProjectPath.ThumbnailFolder;
for (let i = 0; i < photos.length; i++) {
let fullImagePath = path.join(ProjectPath.ImageFolder, photos[i].directory.path, photos[i].directory.name, photos[i].name);
for (let j = 0; j < Config.Client.thumbnailSizes.length; j++) {
let size = Config.Client.thumbnailSizes[j];
let thPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, size));
if (fs.existsSync(thPath) === true) {
if (typeof photos[i].readyThumbnails == "undefined") {
photos[i].readyThumbnails = [];
}
photos[i].readyThumbnails.push(size);
}
}
let iconPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, Config.Client.iconSize));
if (fs.existsSync(iconPath) === true) {
photos[i].readyIcon = true;
}
}
}
public static addThumbnailInformation(req: Request, res: Response, next: NextFunction) {
if (!req.resultPipe)
return next();
let cw: ContentWrapper = req.resultPipe;
if (cw.directory) {
ThumbnailGeneratorMWs.addThInfoTODir(cw.directory);
}
if (cw.searchResult) {
ThumbnailGeneratorMWs.addThInfoToPhotos(cw.searchResult.photos);
}
return next();
}
public static generateThumbnail(req: Request, res: Response, next: NextFunction) {
if (!req.resultPipe)
return next();
//load parameters
let imagePath = req.resultPipe;
let size: number = parseInt(req.params.size) || Config.Client.thumbnailSizes[0];
//validate size
if (Config.Client.thumbnailSizes.indexOf(size) === -1) {
size = Config.Client.thumbnailSizes[0];
}
ThumbnailGeneratorMWs.generateImage(imagePath, size, false, req, res, next);
}
public static generateIcon(req: Request, res: Response, next: NextFunction) {
if (!req.resultPipe)
return next();
//load parameters
let imagePath = req.resultPipe;
let size: number = Config.Client.iconSize;
ThumbnailGeneratorMWs.generateImage(imagePath, size, true, req, res, next);
}
private static generateImage(imagePath: string, size: number, makeSquare: boolean, req: Request, res: Response, next: NextFunction) {
//generate thumbnail path
let thPath = path.join(ProjectPath.ThumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(imagePath, size));
req.resultPipe = thPath;
//check if thumbnail already exist
if (fs.existsSync(thPath) === true) { if (fs.existsSync(thPath) === true) {
return next(); if (typeof photos[i].readyThumbnails == "undefined") {
photos[i].readyThumbnails = [];
}
photos[i].readyThumbnails.push(size);
} }
}
let iconPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, Config.Client.iconSize));
if (fs.existsSync(iconPath) === true) {
photos[i].readyIcon = true;
}
//create thumbnail folder if not exist }
if (!fs.existsSync(ProjectPath.ThumbnailFolder)) { }
fs.mkdirSync(ProjectPath.ThumbnailFolder);
}
this.initPools(); public static addThumbnailInformation(req: Request, res: Response, next: NextFunction) {
//run on other thread if (!req.resultPipe)
return next();
let input = <RendererInput>{ let cw: ContentWrapper = req.resultPipe;
imagePath: imagePath, if (cw.directory) {
size: size, ThumbnailGeneratorMWs.addThInfoTODir(cw.directory);
thPath: thPath, }
makeSquare: makeSquare, if (cw.searchResult) {
qualityPriority: Config.Server.thumbnail.qualityPriority, ThumbnailGeneratorMWs.addThInfoToPhotos(cw.searchResult.photos);
__dirname: __dirname,
};
if (Config.Server.enableThreading == true) {
pool.send(input)
.on('done', (out) => {
return next(out);
}).on('error', (error) => {
console.log(error);
return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, error));
});
} else {
try {
if (Config.Server.thumbnail.hardwareAcceleration == true) {
hardwareRenderer(input, out => next(out));
} else {
softwareRenderer(input, out => next(out));
}
}catch (error){
console.log(error);
return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, error));
}
}
} }
private static generateThumbnailName(imagePath: string, size: number): string {
return crypto.createHash('md5').update(imagePath).digest('hex') + "_" + size + ".jpg"; return next();
}
public static generateThumbnail(req: Request, res: Response, next: NextFunction) {
if (!req.resultPipe)
return next();
//load parameters
let imagePath = req.resultPipe;
let size: number = parseInt(req.params.size) || Config.Client.thumbnailSizes[0];
//validate size
if (Config.Client.thumbnailSizes.indexOf(size) === -1) {
size = Config.Client.thumbnailSizes[0];
} }
ThumbnailGeneratorMWs.generateImage(imagePath, size, false, req, res, next);
}
public static generateIcon(req: Request, res: Response, next: NextFunction) {
if (!req.resultPipe)
return next();
//load parameters
let imagePath = req.resultPipe;
let size: number = Config.Client.iconSize;
ThumbnailGeneratorMWs.generateImage(imagePath, size, true, req, res, next);
}
private static generateImage(imagePath: string, size: number, makeSquare: boolean, req: Request, res: Response, next: NextFunction) {
//generate thumbnail path
let thPath = path.join(ProjectPath.ThumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(imagePath, size));
req.resultPipe = thPath;
//check if thumbnail already exist
if (fs.existsSync(thPath) === true) {
return next();
}
//create thumbnail folder if not exist
if (!fs.existsSync(ProjectPath.ThumbnailFolder)) {
fs.mkdirSync(ProjectPath.ThumbnailFolder);
}
this.initPools();
//run on other thread
let input = <RendererInput>{
imagePath: imagePath,
size: size,
thPath: thPath,
makeSquare: makeSquare,
qualityPriority: Config.Server.thumbnail.qualityPriority,
__dirname: __dirname,
};
if (Config.Server.enableThreading == true) {
pool.send(input)
.on('done', (out) => {
return next(out);
}).on('error', (error) => {
console.log(error);
return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, error));
});
} else {
try {
ThumbnailGeneratorMWs.ThumbnailFunction(input, out => next(out));
} catch (error) {
console.log(error);
return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, error));
}
}
}
private static generateThumbnailName(imagePath: string, size: number): string {
return crypto.createHash('md5').update(imagePath).digest('hex') + "_" + size + ".jpg";
}
} }

View File

@ -13,7 +13,7 @@ import {SharingRouter} from "./routes/SharingRouter";
import {ObjectManagerRepository} from "./model/ObjectManagerRepository"; import {ObjectManagerRepository} from "./model/ObjectManagerRepository";
import {Logger} from "./Logger"; import {Logger} from "./Logger";
import {Config} from "../common/config/private/Config"; import {Config} from "../common/config/private/Config";
import {DatabaseType} from "../common/config/private/IPrivateConfig"; import {DatabaseType, ThumbnailProcessingLib} from "../common/config/private/IPrivateConfig";
const LOG_TAG = "[server]"; const LOG_TAG = "[server]";
export class Server { export class Server {
@ -98,7 +98,7 @@ export class Server {
await ObjectManagerRepository.InitMemoryManagers(); await ObjectManagerRepository.InitMemoryManagers();
} }
if (Config.Server.thumbnail.hardwareAcceleration == true) { if (Config.Server.thumbnail.processingLibrary == ThumbnailProcessingLib.sharp) {
try { try {
const sharp = require("sharp"); const sharp = require("sharp");
sharp(); sharp();
@ -109,7 +109,29 @@ export class Server {
Logger.warn(LOG_TAG, "Thumbnail hardware acceleration is not possible." + Logger.warn(LOG_TAG, "Thumbnail hardware acceleration is not possible." +
" 'Sharp' node module is not found." + " 'Sharp' node module is not found." +
" Falling back to JS based thumbnail generation"); " Falling back to JS based thumbnail generation");
Config.Server.thumbnail.hardwareAcceleration = false; Config.Server.thumbnail.processingLibrary = ThumbnailProcessingLib.Jimp;
}
}
if (Config.Server.thumbnail.processingLibrary == ThumbnailProcessingLib.gm) {
try {
const gm = require("gm");
gm(1, 1).stream((err) => {
Logger.warn(LOG_TAG, "[Thumbnail hardware acceleration] gm module error: ", err);
Logger.warn(LOG_TAG, "Thumbnail hardware acceleration is not possible." +
" 'gm' node module is not found." +
" Falling back to JS based thumbnail generation");
Config.Server.thumbnail.processingLibrary = ThumbnailProcessingLib.Jimp;
});
} catch (err) {
Logger.warn(LOG_TAG, "[Thumbnail hardware acceleration] gm module error: ", err);
Logger.warn(LOG_TAG, "Thumbnail hardware acceleration is not possible." +
" 'gm' node module is not found." +
" Falling back to JS based thumbnail generation");
Config.Server.thumbnail.processingLibrary = ThumbnailProcessingLib.Jimp;
} }
} }

View File

@ -7,6 +7,5 @@ export let Config = new PrivateConfigClass();
ConfigLoader.loadBackendConfig(Config, ConfigLoader.loadBackendConfig(Config,
path.join(__dirname, './../../../config.json'), path.join(__dirname, './../../../config.json'),
[["PORT", "Server-port"]]); [["PORT", "Server-port"]]);

View File

@ -1,30 +1,36 @@
export enum DatabaseType{ export enum DatabaseType{
memory = 0, mysql = 1 memory = 0, mysql = 1
} }
export enum LogLevel { export enum LogLevel {
error, warn, info, debug, verbose error, warn, info, debug, verbose
}
export enum ThumbnailProcessingLib{
Jimp = 0,
gm = 1,
sharp = 2
} }
export interface MySQLConfig { export interface MySQLConfig {
host: string; host: string;
database: string; database: string;
username: string; username: string;
password: string; password: string;
} }
export interface DataBaseConfig { export interface DataBaseConfig {
type: DatabaseType; type: DatabaseType;
mysql?: MySQLConfig; mysql?: MySQLConfig;
} }
export interface ThumbnailConfig { export interface ThumbnailConfig {
folder: string; folder: string;
hardwareAcceleration: boolean; processingLibrary: ThumbnailProcessingLib;
qualityPriority: boolean; qualityPriority: boolean;
} }
export interface ServerConfig { export interface ServerConfig {
port: number; port: number;
imagesFolder: string; imagesFolder: string;
thumbnail: ThumbnailConfig; thumbnail: ThumbnailConfig;
database: DataBaseConfig; database: DataBaseConfig;
enableThreading:boolean; enableThreading: boolean;
} }

View File

@ -1,5 +1,5 @@
import {PublicConfigClass} from "../public/ConfigClass"; import {PublicConfigClass} from "../public/ConfigClass";
import {DatabaseType, ServerConfig} from "./IPrivateConfig"; import {DatabaseType, ServerConfig, ThumbnailProcessingLib} from "./IPrivateConfig";
/** /**
@ -7,35 +7,35 @@ import {DatabaseType, ServerConfig} from "./IPrivateConfig";
*/ */
export class PrivateConfigClass extends PublicConfigClass { export class PrivateConfigClass extends PublicConfigClass {
public Server: ServerConfig = { public Server: ServerConfig = {
port: 80, port: 80,
imagesFolder: "demo/images", imagesFolder: "demo/images",
thumbnail: { thumbnail: {
folder: "demo/TEMP", folder: "demo/TEMP",
hardwareAcceleration: true, processingLibrary: ThumbnailProcessingLib.sharp,
qualityPriority: true qualityPriority: true
}, },
database: { database: {
type: DatabaseType.mysql, type: DatabaseType.mysql,
mysql: { mysql: {
host: "localhost", host: "localhost",
username: "root", username: "root",
password: "root", password: "root",
database: "pigallery2" database: "pigallery2"
} }
}, },
enableThreading: true enableThreading: true
}; };
public setDatabaseType(type: DatabaseType) { public setDatabaseType(type: DatabaseType) {
this.Server.database.type = type; this.Server.database.type = type;
if (type === DatabaseType.memory) { if (type === DatabaseType.memory) {
this.Client.Search.searchEnabled = false; this.Client.Search.searchEnabled = false;
this.Client.Search.instantSearchEnabled = false; this.Client.Search.instantSearchEnabled = false;
this.Client.Search.autocompleteEnabled = false; this.Client.Search.autocompleteEnabled = false;
}
} }
}
} }

View File

@ -3,13 +3,12 @@ import {WebConfigLoader} from "typeconfig/src/WebConfigLoader";
declare module ServerInject { declare module ServerInject {
export const ConfigInject; export const ConfigInject;
} }
export let Config = new PublicConfigClass(); export let Config = new PublicConfigClass();
if (typeof ServerInject !== "undefined" && typeof ServerInject.ConfigInject !== "undefined") { if (typeof ServerInject !== "undefined" && typeof ServerInject.ConfigInject !== "undefined") {
WebConfigLoader.loadFrontendConfig(Config.Client, ServerInject.ConfigInject); WebConfigLoader.loadFrontendConfig(Config.Client, ServerInject.ConfigInject);
} }

View File

@ -1,19 +1,19 @@
interface SearchConfig { interface SearchConfig {
searchEnabled: boolean searchEnabled: boolean
instantSearchEnabled: boolean instantSearchEnabled: boolean
autocompleteEnabled: boolean autocompleteEnabled: boolean
} }
interface ClientConfig { interface ClientConfig {
iconSize: number; iconSize: number;
thumbnailSizes: Array<number>; thumbnailSizes: Array<number>;
Search: SearchConfig; Search: SearchConfig;
concurrentThumbnailGenerations: number; concurrentThumbnailGenerations: number;
enableCache: boolean; enableCache: boolean;
enableOnScrollRendering: boolean; enableOnScrollRendering: boolean;
enableOnScrollThumbnailPrioritising: boolean; enableOnScrollThumbnailPrioritising: boolean;
authenticationRequired: boolean; authenticationRequired: boolean;
googleApiKey: string; googleApiKey: string;
} }
/** /**
@ -21,21 +21,21 @@ interface ClientConfig {
*/ */
export class PublicConfigClass { export class PublicConfigClass {
public Client: ClientConfig = { public Client: ClientConfig = {
thumbnailSizes: [200, 400, 600], thumbnailSizes: [200, 400, 600],
iconSize: 30, iconSize: 30,
Search: { Search: {
searchEnabled: true, searchEnabled: true,
instantSearchEnabled: true, instantSearchEnabled: true,
autocompleteEnabled: true autocompleteEnabled: true
}, },
concurrentThumbnailGenerations: 1, concurrentThumbnailGenerations: 1,
enableCache: false, enableCache: false,
enableOnScrollRendering: true, enableOnScrollRendering: true,
enableOnScrollThumbnailPrioritising: true, enableOnScrollThumbnailPrioritising: true,
authenticationRequired: true, authenticationRequired: true,
googleApiKey: "" googleApiKey: ""
}; };
} }

View File

@ -1,5 +1,11 @@
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic"; import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
import {enableProdMode} from "@angular/core";
import {environment} from "./environments/environment";
import {AppModule} from "./app/app.module"; import {AppModule} from "./app/app.module";
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err)); platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));

View File

@ -63,6 +63,7 @@
"@angular/language-service": "^4.0.0", "@angular/language-service": "^4.0.0",
"@types/express": "^4.0.35", "@types/express": "^4.0.35",
"@types/express-session": "1.15.0", "@types/express-session": "1.15.0",
"@types/gm": "^1.17.31",
"@types/jasmine": "^2.5.51", "@types/jasmine": "^2.5.51",
"@types/node": "^7.0.27", "@types/node": "^7.0.27",
"@types/optimist": "0.0.29", "@types/optimist": "0.0.29",
@ -97,6 +98,7 @@
"typescript": "^2.3.4" "typescript": "^2.3.4"
}, },
"optionalDependencies": { "optionalDependencies": {
"gm": "^1.23.0",
"sharp": "^0.18.1" "sharp": "^0.18.1"
} }
} }