Skip to content

Commit

Permalink
Adds main events to extension #753
Browse files Browse the repository at this point in the history
  • Loading branch information
bpatrik committed Oct 31, 2023
1 parent 538593e commit f7dba92
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 129 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ locale.source.xlf
test.*
/db/
/test/cypress/screenshots/
/extensions/
42 changes: 37 additions & 5 deletions src/backend/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,42 @@ const forcedDebug = process.env['NODE_ENV'] === 'debug';

if (forcedDebug === true) {
console.log(
'NODE_ENV environmental variable is set to debug, forcing all logs to print'
'NODE_ENV environmental variable is set to debug, forcing all logs to print'
);
}

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 {
public static silly(...args: (string | number)[]): void {
if (!forcedDebug && Config.Server.Log.level < LogLevel.silly) {
Expand Down Expand Up @@ -55,10 +87,10 @@ export class Logger {
const date = new Date().toLocaleString();
let LOG_TAG = '';
if (
args.length > 0 &&
typeof args[0] === 'string' &&
args[0].startsWith('[') &&
args[0].endsWith(']')
args.length > 0 &&
typeof args[0] === 'string' &&
args[0].startsWith('[') &&
args[0].endsWith(']')
) {
LOG_TAG = args[0];
args.shift();
Expand Down
8 changes: 4 additions & 4 deletions src/backend/ProjectPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ export class ProjectPathClass {
this.reset();
}

normalizeRelative(pathStr: string): string {
public normalizeRelative(pathStr: string): string {
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);
}

getRelativePathToImages(pathStr: string): string {
public getRelativePathToImages(pathStr: string): string {
return path.relative(this.ImageFolder, pathStr);
}

Expand All @@ -36,7 +36,7 @@ export class ProjectPathClass {
this.TranscodedFolder = path.join(this.TempFolder, 'tc');
this.FacesFolder = path.join(this.TempFolder, 'f');
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
if (!fs.existsSync(this.TempFolder)) {
Expand Down
2 changes: 1 addition & 1 deletion src/backend/model/database/AlbumManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class AlbumManager implements IObjectManager {
private static async updateAlbum(album: SavedSearchEntity): Promise<void> {
const connection = await SQLConnection.getConnection();
const cover =
await ObjectManagers.getInstance().CoverManager.getAlbumCover(album);
await ObjectManagers.getInstance().CoverManager.getCoverForAlbum(album);
const count = await
ObjectManagers.getInstance().SearchManager.getCount((album as SavedSearchDTO).searchQuery);

Expand Down
194 changes: 106 additions & 88 deletions src/backend/model/database/CoverManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {CoverPhotoDTO} from '../../../common/entities/PhotoDTO';
import {IObjectManager} from './IObjectManager';
import {Logger} from '../../Logger';
import {SearchManager} from './SearchManager';
import {ExtensionDecorator} from '../extension/ExtensionDecorator';

const LOG_TAG = '[CoverManager]';

Expand All @@ -29,21 +30,22 @@ export class CoverManager implements IObjectManager {
public async resetCovers(): Promise<void> {
const connection = await SQLConnection.getConnection();
await connection
.createQueryBuilder()
.update(DirectoryEntity)
.set({validCover: false})
.execute();
.createQueryBuilder()
.update(DirectoryEntity)
.set({validCover: false})
.execute();
}

public async onNewDataVersion(changedDir: ParentDirectoryDTO): Promise<void> {
@ExtensionDecorator(e => e.gallery.CoverManager.invalidateDirectoryCovers)
protected async invalidateDirectoryCovers(dir: ParentDirectoryDTO) {
// Invalidating Album cover
let fullPath = DiskManager.normalizeDirPath(
path.join(changedDir.path, changedDir.name)
path.join(dir.path, dir.name)
);
const query = (await SQLConnection.getConnection())
.createQueryBuilder()
.update(DirectoryEntity)
.set({validCover: false});
.createQueryBuilder()
.update(DirectoryEntity)
.set({validCover: false});

let i = 0;
const root = DiskManager.pathFromRelativeDirName('.');
Expand All @@ -53,62 +55,67 @@ export class CoverManager implements IObjectManager {
fullPath = parentPath;
++i;
query.orWhere(
new Brackets((q: WhereExpression) => {
const param: { [key: string]: string } = {};
param['name' + i] = name;
param['path' + i] = parentPath;
q.where(`path = :path${i}`, param);
q.andWhere(`name = :name${i}`, param);
})
);
}

++i;
query.orWhere(
new Brackets((q: WhereExpression) => {
const param: { [key: string]: string } = {};
param['name' + i] = DiskManager.dirName('.');
param['path' + i] = DiskManager.pathFromRelativeDirName('.');
param['name' + i] = name;
param['path' + i] = parentPath;
q.where(`path = :path${i}`, param);
q.andWhere(`name = :name${i}`, param);
})
);
}

++i;
query.orWhere(
new Brackets((q: WhereExpression) => {
const param: { [key: string]: string } = {};
param['name' + i] = DiskManager.dirName('.');
param['path' + i] = DiskManager.pathFromRelativeDirName('.');
q.where(`path = :path${i}`, param);
q.andWhere(`name = :name${i}`, param);
})
);

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;
}): Promise<CoverPhotoDTOWithID> {
const albumQuery: Brackets = await
ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(album.searchQuery);
ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(album.searchQuery);
const connection = await SQLConnection.getConnection();

const coverQuery = (): SelectQueryBuilder<MediaEntity> => {
const query = connection
.getRepository(MediaEntity)
.createQueryBuilder('media')
.innerJoin('media.directory', 'directory')
.select(['media.name', 'media.id', ...CoverManager.DIRECTORY_SELECT])
.where(albumQuery);
.getRepository(MediaEntity)
.createQueryBuilder('media')
.innerJoin('media.directory', 'directory')
.select(['media.name', 'media.id', ...CoverManager.DIRECTORY_SELECT])
.where(albumQuery);
SearchManager.setSorting(query, Config.AlbumCover.Sorting);
return query;
};
let coverMedia = null;
if (
Config.AlbumCover.SearchQuery &&
!Utils.equalsFilter(Config.AlbumCover.SearchQuery, {
type: SearchQueryTypes.any_text,
text: '',
} as TextSearch)
Config.AlbumCover.SearchQuery &&
!Utils.equalsFilter(Config.AlbumCover.SearchQuery, {
type: SearchQueryTypes.any_text,
text: '',
} as TextSearch)
) {
try {
const coverFilterQuery = await
ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(Config.AlbumCover.SearchQuery);
ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(Config.AlbumCover.SearchQuery);
coverMedia = await coverQuery()
.andWhere(coverFilterQuery)
.limit(1)
.getOne();
.andWhere(coverFilterQuery)
.limit(1)
.getOne();
} catch (e) {
Logger.error(LOG_TAG, 'Cant get album cover using:', JSON.stringify(album.searchQuery), JSON.stringify(Config.AlbumCover.SearchQuery));
throw e;
Expand All @@ -127,52 +134,53 @@ export class CoverManager implements IObjectManager {
}

public async getPartialDirsWithoutCovers(): Promise<
{ id: number; name: string; path: string }[]
{ id: number; name: string; path: string }[]
> {
const connection = await SQLConnection.getConnection();
return await connection
.getRepository(DirectoryEntity)
.createQueryBuilder('directory')
.where('directory.validCover = :validCover', {validCover: 0}) // 0 === false
.select(['name', 'id', 'path'])
.getRawMany();
.getRepository(DirectoryEntity)
.createQueryBuilder('directory')
.where('directory.validCover = :validCover', {validCover: 0}) // 0 === false
.select(['name', 'id', 'path'])
.getRawMany();
}

public async setAndGetCoverForDirectory(dir: {
@ExtensionDecorator(e => e.gallery.CoverManager.getCoverForDirectory)
protected async getCoverForDirectory(dir: {
id: number;
name: string;
path: string;
}): Promise<CoverPhotoDTOWithID> {
}) {
const connection = await SQLConnection.getConnection();
const coverQuery = (): SelectQueryBuilder<MediaEntity> => {
const query = connection
.getRepository(MediaEntity)
.createQueryBuilder('media')
.innerJoin('media.directory', 'directory')
.select(['media.name', 'media.id', ...CoverManager.DIRECTORY_SELECT])
.where(
new Brackets((q: WhereExpression) => {
q.where('media.directory = :dir', {
dir: dir.id,
});
if (Config.Database.type === DatabaseType.mysql) {
q.orWhere('directory.path like :path || \'%\'', {
path: DiskManager.pathFromParent(dir),
});
} else {
q.orWhere('directory.path GLOB :path', {
path: DiskManager.pathFromParent(dir)
// glob escaping. see https://github.com/bpatrik/pigallery2/issues/621
.replaceAll('[', '[[]') + '*',
});
}
})
);
.getRepository(MediaEntity)
.createQueryBuilder('media')
.innerJoin('media.directory', 'directory')
.select(['media.name', 'media.id', ...CoverManager.DIRECTORY_SELECT])
.where(
new Brackets((q: WhereExpression) => {
q.where('media.directory = :dir', {
dir: dir.id,
});
if (Config.Database.type === DatabaseType.mysql) {
q.orWhere('directory.path like :path || \'%\'', {
path: DiskManager.pathFromParent(dir),
});
} else {
q.orWhere('directory.path GLOB :path', {
path: DiskManager.pathFromParent(dir)
// glob escaping. see https://github.com/bpatrik/pigallery2/issues/621
.replaceAll('[', '[[]') + '*',
});
}
})
);
// Select from the directory if any otherwise from any subdirectories.
// (There is no priority between subdirectories)
query.orderBy(
`CASE WHEN directory.id = ${dir.id} THEN 0 ELSE 1 END`,
'ASC'
`CASE WHEN directory.id = ${dir.id} THEN 0 ELSE 1 END`,
'ASC'
);

SearchManager.setSorting(query, Config.AlbumCover.Sorting);
Expand All @@ -181,33 +189,43 @@ export class CoverManager implements IObjectManager {

let coverMedia: CoverPhotoDTOWithID = null;
if (
Config.AlbumCover.SearchQuery &&
!Utils.equalsFilter(Config.AlbumCover.SearchQuery, {
type: SearchQueryTypes.any_text,
text: '',
} as TextSearch)
Config.AlbumCover.SearchQuery &&
!Utils.equalsFilter(Config.AlbumCover.SearchQuery, {
type: SearchQueryTypes.any_text,
text: '',
} as TextSearch)
) {
coverMedia = await coverQuery()
.andWhere(
await ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(Config.AlbumCover.SearchQuery)
)
.limit(1)
.getOne();
.andWhere(
await ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(Config.AlbumCover.SearchQuery)
)
.limit(1)
.getOne();
}

if (!coverMedia) {
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)
await connection
.createQueryBuilder()
.update(DirectoryEntity)
.set({cover: coverMedia, validCover: true})
.where('id = :dir', {
dir: dir.id,
})
.execute();
.createQueryBuilder()
.update(DirectoryEntity)
.set({cover: coverMedia, validCover: true})
.where('id = :dir', {
dir: dir.id,
})
.execute();

return coverMedia || null;
}
Expand Down
Loading

0 comments on commit f7dba92

Please sign in to comment.