You've already forked pigallery2
mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-07-15 01:24:25 +02:00
Adds main events to extension #753
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,3 +28,4 @@ locale.source.xlf
|
|||||||
test.*
|
test.*
|
||||||
/db/
|
/db/
|
||||||
/test/cypress/screenshots/
|
/test/cypress/screenshots/
|
||||||
|
/extensions/
|
||||||
|
@ -11,6 +11,38 @@ if (forcedDebug === true) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LoggerFunction = (...args: (string | number)[]) => void;
|
||||||
|
|
||||||
|
export interface ILogger {
|
||||||
|
silly: LoggerFunction;
|
||||||
|
debug: LoggerFunction;
|
||||||
|
verbose: LoggerFunction;
|
||||||
|
info: LoggerFunction;
|
||||||
|
warn: LoggerFunction;
|
||||||
|
error: LoggerFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createLoggerWrapper = (TAG: string): ILogger => ({
|
||||||
|
silly: (...args: (string | number)[]) => {
|
||||||
|
Logger.silly(TAG, ...args);
|
||||||
|
},
|
||||||
|
debug: (...args: (string | number)[]) => {
|
||||||
|
Logger.debug(TAG, ...args);
|
||||||
|
},
|
||||||
|
verbose: (...args: (string | number)[]) => {
|
||||||
|
Logger.verbose(TAG, ...args);
|
||||||
|
},
|
||||||
|
info: (...args: (string | number)[]) => {
|
||||||
|
Logger.info(TAG, ...args);
|
||||||
|
},
|
||||||
|
warn: (...args: (string | number)[]) => {
|
||||||
|
Logger.warn(TAG, ...args);
|
||||||
|
},
|
||||||
|
error: (...args: (string | number)[]) => {
|
||||||
|
Logger.error(TAG, ...args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export class Logger {
|
export class Logger {
|
||||||
public static silly(...args: (string | number)[]): void {
|
public static silly(...args: (string | number)[]): void {
|
||||||
if (!forcedDebug && Config.Server.Log.level < LogLevel.silly) {
|
if (!forcedDebug && Config.Server.Log.level < LogLevel.silly) {
|
||||||
|
@ -16,15 +16,15 @@ export class ProjectPathClass {
|
|||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeRelative(pathStr: string): string {
|
public normalizeRelative(pathStr: string): string {
|
||||||
return path.join(pathStr, path.sep);
|
return path.join(pathStr, path.sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAbsolutePath(pathStr: string): string {
|
public getAbsolutePath(pathStr: string): string {
|
||||||
return path.isAbsolute(pathStr) ? pathStr : path.join(this.Root, pathStr);
|
return path.isAbsolute(pathStr) ? pathStr : path.join(this.Root, pathStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRelativePathToImages(pathStr: string): string {
|
public getRelativePathToImages(pathStr: string): string {
|
||||||
return path.relative(this.ImageFolder, pathStr);
|
return path.relative(this.ImageFolder, pathStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ export class ProjectPathClass {
|
|||||||
this.TranscodedFolder = path.join(this.TempFolder, 'tc');
|
this.TranscodedFolder = path.join(this.TempFolder, 'tc');
|
||||||
this.FacesFolder = path.join(this.TempFolder, 'f');
|
this.FacesFolder = path.join(this.TempFolder, 'f');
|
||||||
this.DBFolder = this.getAbsolutePath(Config.Database.dbFolder);
|
this.DBFolder = this.getAbsolutePath(Config.Database.dbFolder);
|
||||||
this.ExtensionFolder = path.join(this.Root, 'extension');
|
this.ExtensionFolder = path.join(this.Root, 'extensions');
|
||||||
|
|
||||||
// create thumbnail folder if not exist
|
// create thumbnail folder if not exist
|
||||||
if (!fs.existsSync(this.TempFolder)) {
|
if (!fs.existsSync(this.TempFolder)) {
|
||||||
|
@ -19,7 +19,7 @@ export class AlbumManager implements IObjectManager {
|
|||||||
private static async updateAlbum(album: SavedSearchEntity): Promise<void> {
|
private static async updateAlbum(album: SavedSearchEntity): Promise<void> {
|
||||||
const connection = await SQLConnection.getConnection();
|
const connection = await SQLConnection.getConnection();
|
||||||
const cover =
|
const cover =
|
||||||
await ObjectManagers.getInstance().CoverManager.getAlbumCover(album);
|
await ObjectManagers.getInstance().CoverManager.getCoverForAlbum(album);
|
||||||
const count = await
|
const count = await
|
||||||
ObjectManagers.getInstance().SearchManager.getCount((album as SavedSearchDTO).searchQuery);
|
ObjectManagers.getInstance().SearchManager.getCount((album as SavedSearchDTO).searchQuery);
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import {CoverPhotoDTO} from '../../../common/entities/PhotoDTO';
|
|||||||
import {IObjectManager} from './IObjectManager';
|
import {IObjectManager} from './IObjectManager';
|
||||||
import {Logger} from '../../Logger';
|
import {Logger} from '../../Logger';
|
||||||
import {SearchManager} from './SearchManager';
|
import {SearchManager} from './SearchManager';
|
||||||
|
import {ExtensionDecorator} from '../extension/ExtensionDecorator';
|
||||||
|
|
||||||
const LOG_TAG = '[CoverManager]';
|
const LOG_TAG = '[CoverManager]';
|
||||||
|
|
||||||
@ -35,10 +36,11 @@ export class CoverManager implements IObjectManager {
|
|||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onNewDataVersion(changedDir: ParentDirectoryDTO): Promise<void> {
|
@ExtensionDecorator(e => e.gallery.CoverManager.invalidateDirectoryCovers)
|
||||||
|
protected async invalidateDirectoryCovers(dir: ParentDirectoryDTO) {
|
||||||
// Invalidating Album cover
|
// Invalidating Album cover
|
||||||
let fullPath = DiskManager.normalizeDirPath(
|
let fullPath = DiskManager.normalizeDirPath(
|
||||||
path.join(changedDir.path, changedDir.name)
|
path.join(dir.path, dir.name)
|
||||||
);
|
);
|
||||||
const query = (await SQLConnection.getConnection())
|
const query = (await SQLConnection.getConnection())
|
||||||
.createQueryBuilder()
|
.createQueryBuilder()
|
||||||
@ -77,7 +79,12 @@ export class CoverManager implements IObjectManager {
|
|||||||
await query.execute();
|
await query.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAlbumCover(album: {
|
public async onNewDataVersion(changedDir: ParentDirectoryDTO): Promise<void> {
|
||||||
|
await this.invalidateDirectoryCovers(changedDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExtensionDecorator(e => e.gallery.CoverManager.getCoverForAlbum)
|
||||||
|
public async getCoverForAlbum(album: {
|
||||||
searchQuery: SearchQueryDTO;
|
searchQuery: SearchQueryDTO;
|
||||||
}): Promise<CoverPhotoDTOWithID> {
|
}): Promise<CoverPhotoDTOWithID> {
|
||||||
const albumQuery: Brackets = await
|
const albumQuery: Brackets = await
|
||||||
@ -138,11 +145,12 @@ export class CoverManager implements IObjectManager {
|
|||||||
.getRawMany();
|
.getRawMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setAndGetCoverForDirectory(dir: {
|
@ExtensionDecorator(e => e.gallery.CoverManager.getCoverForDirectory)
|
||||||
|
protected async getCoverForDirectory(dir: {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
}): Promise<CoverPhotoDTOWithID> {
|
}) {
|
||||||
const connection = await SQLConnection.getConnection();
|
const connection = await SQLConnection.getConnection();
|
||||||
const coverQuery = (): SelectQueryBuilder<MediaEntity> => {
|
const coverQuery = (): SelectQueryBuilder<MediaEntity> => {
|
||||||
const query = connection
|
const query = connection
|
||||||
@ -198,6 +206,16 @@ export class CoverManager implements IObjectManager {
|
|||||||
if (!coverMedia) {
|
if (!coverMedia) {
|
||||||
coverMedia = await coverQuery().limit(1).getOne();
|
coverMedia = await coverQuery().limit(1).getOne();
|
||||||
}
|
}
|
||||||
|
return coverMedia;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setAndGetCoverForDirectory(dir: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
}): Promise<CoverPhotoDTOWithID> {
|
||||||
|
const connection = await SQLConnection.getConnection();
|
||||||
|
const coverMedia = await this.getCoverForDirectory(dir);
|
||||||
|
|
||||||
// set validCover bit to true even if there is no cover (to prevent future updates)
|
// set validCover bit to true even if there is no cover (to prevent future updates)
|
||||||
await connection
|
await connection
|
||||||
|
@ -3,7 +3,7 @@ import {Config} from '../../../common/config/private/Config';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {IObjectManager} from '../database/IObjectManager';
|
import {IObjectManager} from '../database/IObjectManager';
|
||||||
import {Logger} from '../../Logger';
|
import {createLoggerWrapper, Logger} from '../../Logger';
|
||||||
import {IExtensionEvents, IExtensionObject, IServerExtension} from './IExtension';
|
import {IExtensionEvents, IExtensionObject, IServerExtension} from './IExtension';
|
||||||
import {ObjectManagers} from '../ObjectManagers';
|
import {ObjectManagers} from '../ObjectManagers';
|
||||||
import {Server} from '../../server';
|
import {Server} from '../../server';
|
||||||
@ -16,7 +16,19 @@ export class ExtensionManager implements IObjectManager {
|
|||||||
events: IExtensionEvents = {
|
events: IExtensionEvents = {
|
||||||
gallery: {
|
gallery: {
|
||||||
MetadataLoader: {
|
MetadataLoader: {
|
||||||
loadPhotoMetadata: new ExtensionEvent()
|
loadPhotoMetadata: new ExtensionEvent(),
|
||||||
|
loadVideoMetadata: new ExtensionEvent()
|
||||||
|
},
|
||||||
|
CoverManager: {
|
||||||
|
getCoverForDirectory: new ExtensionEvent(),
|
||||||
|
getCoverForAlbum: new ExtensionEvent(),
|
||||||
|
invalidateDirectoryCovers: new ExtensionEvent(),
|
||||||
|
},
|
||||||
|
DiskManager: {
|
||||||
|
scanDirectory: new ExtensionEvent()
|
||||||
|
},
|
||||||
|
ImageRenderer: {
|
||||||
|
render: new ExtensionEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -27,15 +39,18 @@ export class ExtensionManager implements IObjectManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public loadExtensionsList() {
|
public loadExtensionsList() {
|
||||||
|
Logger.debug(LOG_TAG, 'Loading extension list from ' + ProjectPath.ExtensionFolder);
|
||||||
if (!fs.existsSync(ProjectPath.ExtensionFolder)) {
|
if (!fs.existsSync(ProjectPath.ExtensionFolder)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.Extensions.list = fs
|
Config.Extensions.list = fs
|
||||||
.readdirSync(ProjectPath.ExtensionFolder)
|
.readdirSync(ProjectPath.ExtensionFolder)
|
||||||
.filter((f): boolean =>
|
.filter((f): boolean =>
|
||||||
fs.statSync(path.join(ProjectPath.ExtensionFolder, f)).isDirectory()
|
fs.statSync(path.join(ProjectPath.ExtensionFolder, f)).isDirectory()
|
||||||
);
|
);
|
||||||
Config.Extensions.list.sort();
|
Config.Extensions.list.sort();
|
||||||
|
Logger.debug(LOG_TAG, 'Extensions found ', JSON.stringify(Config.Extensions.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async callServerFN(fn: (ext: IServerExtension, extName: string) => Promise<void>) {
|
private async callServerFN(fn: (ext: IServerExtension, extName: string) => Promise<void>) {
|
||||||
@ -44,6 +59,7 @@ export class ExtensionManager implements IObjectManager {
|
|||||||
const extPath = path.join(ProjectPath.ExtensionFolder, extName);
|
const extPath = path.join(ProjectPath.ExtensionFolder, extName);
|
||||||
const serverExt = path.join(extPath, 'server.js');
|
const serverExt = path.join(extPath, 'server.js');
|
||||||
if (!fs.existsSync(serverExt)) {
|
if (!fs.existsSync(serverExt)) {
|
||||||
|
Logger.silly(LOG_TAG, `Skipping ${extName} server initiation. server.js does not exists`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
@ -52,23 +68,24 @@ export class ExtensionManager implements IObjectManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createExtensionObject(): IExtensionObject {
|
private createExtensionObject(name: string): IExtensionObject {
|
||||||
return {
|
return {
|
||||||
app: {
|
_app: {
|
||||||
objectManagers: ObjectManagers.getInstance(),
|
objectManagers: ObjectManagers.getInstance(),
|
||||||
config: Config,
|
expressApp: Server.getInstance().app,
|
||||||
paths: ProjectPath,
|
config: Config
|
||||||
expressApp: Server.getInstance().app
|
|
||||||
},
|
},
|
||||||
events: null
|
paths: ProjectPath,
|
||||||
|
Logger: createLoggerWrapper(`[Extension: ${name}]`),
|
||||||
|
events: this.events
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initExtensions() {
|
private async initExtensions() {
|
||||||
await this.callServerFN(async (ext, extName) => {
|
await this.callServerFN(async (ext, extName) => {
|
||||||
if (typeof ext?.init === 'function') {
|
if (typeof ext?.init === 'function') {
|
||||||
Logger.debug(LOG_TAG, 'Running Init on extension:' + extName);
|
Logger.debug(LOG_TAG, 'Running init on extension: ' + extName);
|
||||||
await ext?.init(this.createExtensionObject());
|
await ext?.init(this.createExtensionObject(extName));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -77,7 +94,7 @@ export class ExtensionManager implements IObjectManager {
|
|||||||
await this.callServerFN(async (ext, extName) => {
|
await this.callServerFN(async (ext, extName) => {
|
||||||
if (typeof ext?.cleanUp === 'function') {
|
if (typeof ext?.cleanUp === 'function') {
|
||||||
Logger.debug(LOG_TAG, 'Running Init on extension:' + extName);
|
Logger.debug(LOG_TAG, 'Running Init on extension:' + extName);
|
||||||
await ext?.cleanUp();
|
await ext?.cleanUp(this.createExtensionObject(extName));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import * as express from 'express';
|
|||||||
import {PrivateConfigClass} from '../../../common/config/private/Config';
|
import {PrivateConfigClass} from '../../../common/config/private/Config';
|
||||||
import {ObjectManagers} from '../ObjectManagers';
|
import {ObjectManagers} from '../ObjectManagers';
|
||||||
import {ProjectPathClass} from '../../ProjectPath';
|
import {ProjectPathClass} from '../../ProjectPath';
|
||||||
import {ExtensionEvent} from './ExtensionEvent';
|
import {ILogger} from '../../Logger';
|
||||||
|
|
||||||
|
|
||||||
export type IExtensionBeforeEventHandler<I, O> = (input: { inputs: I }, event: { stopPropagation: boolean }) => Promise<{ inputs: I } | O>;
|
export type IExtensionBeforeEventHandler<I, O> = (input: { inputs: I }, event: { stopPropagation: boolean }) => Promise<{ inputs: I } | O>;
|
||||||
@ -11,36 +11,66 @@ export type IExtensionAfterEventHandler<O> = (output: O) => Promise<O>;
|
|||||||
|
|
||||||
export interface IExtensionEvent<I, O> {
|
export interface IExtensionEvent<I, O> {
|
||||||
before: (handler: IExtensionBeforeEventHandler<I, O>) => void;
|
before: (handler: IExtensionBeforeEventHandler<I, O>) => void;
|
||||||
after: (handler: IExtensionAfterEventHandler<O>) => void
|
after: (handler: IExtensionAfterEventHandler<O>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All main event callbacks in the app
|
||||||
|
*/
|
||||||
export interface IExtensionEvents {
|
export interface IExtensionEvents {
|
||||||
gallery: {
|
gallery: {
|
||||||
// indexing: IExtensionEvent<any, any>;
|
/**
|
||||||
// scanningDirectory: IExtensionEvent<any, any>;
|
* Events for Directory and Album covers
|
||||||
|
*/
|
||||||
|
CoverManager: {
|
||||||
|
getCoverForAlbum: IExtensionEvent<any, any>;
|
||||||
|
getCoverForDirectory: IExtensionEvent<any, any>
|
||||||
|
/**
|
||||||
|
* Invalidates directory covers for a given directory and every parent
|
||||||
|
*/
|
||||||
|
invalidateDirectoryCovers: IExtensionEvent<any, any>;
|
||||||
|
},
|
||||||
|
ImageRenderer: {
|
||||||
|
/**
|
||||||
|
* Renders a thumbnail or photo
|
||||||
|
*/
|
||||||
|
render: IExtensionEvent<any, any>
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Reads exif, iptc, etc.. metadata for photos/videos
|
||||||
|
*/
|
||||||
MetadataLoader: {
|
MetadataLoader: {
|
||||||
|
loadVideoMetadata: IExtensionEvent<any, any>,
|
||||||
loadPhotoMetadata: IExtensionEvent<any, any>
|
loadPhotoMetadata: IExtensionEvent<any, any>
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Scans the storage for a given directory and returns the list of child directories,
|
||||||
|
* photos, videos and metafiles
|
||||||
|
*/
|
||||||
|
DiskManager: {
|
||||||
|
scanDirectory: IExtensionEvent<any, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
//listingDirectory: IExtensionEvent<any, any>;
|
|
||||||
//searching: IExtensionEvent<any, any>;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExtensionApp {
|
export interface IExtensionApp {
|
||||||
expressApp: express.Express;
|
expressApp: express.Express;
|
||||||
config: PrivateConfigClass;
|
|
||||||
objectManagers: ObjectManagers;
|
objectManagers: ObjectManagers;
|
||||||
paths: ProjectPathClass;
|
config: PrivateConfigClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExtensionObject {
|
export interface IExtensionObject {
|
||||||
app: IExtensionApp;
|
_app: IExtensionApp;
|
||||||
|
paths: ProjectPathClass;
|
||||||
|
Logger: ILogger;
|
||||||
events: IExtensionEvents;
|
events: IExtensionEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IServerExtension {
|
|
||||||
init(app: IExtensionObject): Promise<void>;
|
|
||||||
|
|
||||||
cleanUp?: () => Promise<void>;
|
/**
|
||||||
|
* Extension interface. All extension is expected to implement and export these methods
|
||||||
|
*/
|
||||||
|
export interface IServerExtension {
|
||||||
|
init(extension: IExtensionObject): Promise<void>;
|
||||||
|
cleanUp?: (extension: IExtensionObject) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import {GPXProcessing} from './fileprocessing/GPXProcessing';
|
|||||||
import {MDFileDTO} from '../../../common/entities/MDFileDTO';
|
import {MDFileDTO} from '../../../common/entities/MDFileDTO';
|
||||||
import {MetadataLoader} from './MetadataLoader';
|
import {MetadataLoader} from './MetadataLoader';
|
||||||
import {NotificationManager} from '../NotifocationManager';
|
import {NotificationManager} from '../NotifocationManager';
|
||||||
|
import {ExtensionDecorator} from '../extension/ExtensionDecorator';
|
||||||
|
|
||||||
|
|
||||||
const LOG_TAG = '[DiskManager]';
|
const LOG_TAG = '[DiskManager]';
|
||||||
@ -101,6 +102,7 @@ export class DiskManager {
|
|||||||
)) as ParentDirectoryDTO<FileDTO>;
|
)) as ParentDirectoryDTO<FileDTO>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtensionDecorator(e => e.gallery.DiskManager.scanDirectory)
|
||||||
public static async scanDirectory(
|
public static async scanDirectory(
|
||||||
relativeDirectoryName: string,
|
relativeDirectoryName: string,
|
||||||
settings: DirectoryScanSettings = {}
|
settings: DirectoryScanSettings = {}
|
||||||
|
@ -18,6 +18,8 @@ const LOG_TAG = '[MetadataLoader]';
|
|||||||
const ffmpeg = FFmpegFactory.get();
|
const ffmpeg = FFmpegFactory.get();
|
||||||
|
|
||||||
export class MetadataLoader {
|
export class MetadataLoader {
|
||||||
|
|
||||||
|
@ExtensionDecorator(e=>e.gallery.MetadataLoader.loadVideoMetadata)
|
||||||
public static loadVideoMetadata(fullPath: string): Promise<VideoMetadata> {
|
public static loadVideoMetadata(fullPath: string): Promise<VideoMetadata> {
|
||||||
return new Promise<VideoMetadata>((resolve) => {
|
return new Promise<VideoMetadata>((resolve) => {
|
||||||
const metadata: VideoMetadata = {
|
const metadata: VideoMetadata = {
|
||||||
|
@ -5,6 +5,7 @@ import {Logger} from '../../Logger';
|
|||||||
import {FfmpegCommand, FfprobeData} from 'fluent-ffmpeg';
|
import {FfmpegCommand, FfprobeData} from 'fluent-ffmpeg';
|
||||||
import {FFmpegFactory} from '../FFmpegFactory';
|
import {FFmpegFactory} from '../FFmpegFactory';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import {ExtensionDecorator} from '../extension/ExtensionDecorator';
|
||||||
|
|
||||||
|
|
||||||
sharp.cache(false);
|
sharp.cache(false);
|
||||||
@ -129,6 +130,7 @@ export class VideoRendererFactory {
|
|||||||
|
|
||||||
export class ImageRendererFactory {
|
export class ImageRendererFactory {
|
||||||
|
|
||||||
|
@ExtensionDecorator(e=>e.gallery.ImageRenderer.render)
|
||||||
public static async render(input: MediaRendererInput | SvgRendererInput): Promise<void> {
|
public static async render(input: MediaRendererInput | SvgRendererInput): Promise<void> {
|
||||||
|
|
||||||
let image: Sharp;
|
let image: Sharp;
|
||||||
|
@ -35,7 +35,7 @@ export class Server {
|
|||||||
public app: express.Express;
|
public app: express.Express;
|
||||||
private server: HttpServer;
|
private server: HttpServer;
|
||||||
|
|
||||||
static instance: Server;
|
static instance: Server = null;
|
||||||
|
|
||||||
public static getInstance(): Server {
|
public static getInstance(): Server {
|
||||||
if (this.instance === null) {
|
if (this.instance === null) {
|
||||||
@ -70,7 +70,13 @@ export class Server {
|
|||||||
).configPath +
|
).configPath +
|
||||||
':'
|
':'
|
||||||
);
|
);
|
||||||
Logger.verbose(LOG_TAG, JSON.stringify(Config.toJSON({attachDescription: false}), null, '\t'));
|
Logger.verbose(LOG_TAG, JSON.stringify(Config.toJSON({attachDescription: false}), (k, v) => {
|
||||||
|
const MAX_LENGTH = 80;
|
||||||
|
if (typeof v === 'string' && v.length > MAX_LENGTH) {
|
||||||
|
v = v.slice(0, MAX_LENGTH - 3) + '...';
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}, 2));
|
||||||
|
|
||||||
this.app = express();
|
this.app = express();
|
||||||
|
|
||||||
|
@ -212,21 +212,21 @@ describe('CoverManager', (sqlHelper: DBTestHelper) => {
|
|||||||
it('should get cover for saved search', async () => {
|
it('should get cover for saved search', async () => {
|
||||||
const pm = new CoverManager();
|
const pm = new CoverManager();
|
||||||
Config.AlbumCover.SearchQuery = null;
|
Config.AlbumCover.SearchQuery = null;
|
||||||
expect(Utils.clone(await pm.getAlbumCover({
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
||||||
searchQuery: {
|
searchQuery: {
|
||||||
type: SearchQueryTypes.any_text,
|
type: SearchQueryTypes.any_text,
|
||||||
text: 'sw'
|
text: 'sw'
|
||||||
} as TextSearch
|
} as TextSearch
|
||||||
}))).to.deep.equalInAnyOrder(previewifyMedia(p4));
|
}))).to.deep.equalInAnyOrder(previewifyMedia(p4));
|
||||||
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch;
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch;
|
||||||
expect(Utils.clone(await pm.getAlbumCover({
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
||||||
searchQuery: {
|
searchQuery: {
|
||||||
type: SearchQueryTypes.any_text,
|
type: SearchQueryTypes.any_text,
|
||||||
text: 'sw'
|
text: 'sw'
|
||||||
} as TextSearch
|
} as TextSearch
|
||||||
}))).to.deep.equalInAnyOrder(previewifyMedia(p));
|
}))).to.deep.equalInAnyOrder(previewifyMedia(p));
|
||||||
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
|
||||||
expect(Utils.clone(await pm.getAlbumCover({
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
||||||
searchQuery: {
|
searchQuery: {
|
||||||
type: SearchQueryTypes.any_text,
|
type: SearchQueryTypes.any_text,
|
||||||
text: 'sw'
|
text: 'sw'
|
||||||
@ -234,7 +234,7 @@ describe('CoverManager', (sqlHelper: DBTestHelper) => {
|
|||||||
}))).to.deep.equalInAnyOrder(previewifyMedia(p2));
|
}))).to.deep.equalInAnyOrder(previewifyMedia(p2));
|
||||||
// Having a preview search query that does not return valid result
|
// Having a preview search query that does not return valid result
|
||||||
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'wont find it'} as TextSearch;
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'wont find it'} as TextSearch;
|
||||||
expect(Utils.clone(await pm.getAlbumCover({
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
||||||
searchQuery: {
|
searchQuery: {
|
||||||
type: SearchQueryTypes.any_text,
|
type: SearchQueryTypes.any_text,
|
||||||
text: 'Derem'
|
text: 'Derem'
|
||||||
@ -242,7 +242,7 @@ describe('CoverManager', (sqlHelper: DBTestHelper) => {
|
|||||||
}))).to.deep.equalInAnyOrder(previewifyMedia(p2));
|
}))).to.deep.equalInAnyOrder(previewifyMedia(p2));
|
||||||
// having a saved search that does not have any image
|
// having a saved search that does not have any image
|
||||||
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
|
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
|
||||||
expect(Utils.clone(await pm.getAlbumCover({
|
expect(Utils.clone(await pm.getCoverForAlbum({
|
||||||
searchQuery: {
|
searchQuery: {
|
||||||
type: SearchQueryTypes.any_text,
|
type: SearchQueryTypes.any_text,
|
||||||
text: 'wont find it'
|
text: 'wont find it'
|
||||||
|
Reference in New Issue
Block a user