mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
Creating top pick job #683
This commit is contained in:
parent
041b984dbd
commit
35340b2c04
14
package-lock.json
generated
14
package-lock.json
generated
@ -23,6 +23,7 @@
|
|||||||
"image-size": "1.0.2",
|
"image-size": "1.0.2",
|
||||||
"locale": "0.1.0",
|
"locale": "0.1.0",
|
||||||
"node-geocoder": "4.2.0",
|
"node-geocoder": "4.2.0",
|
||||||
|
"nodemailer": "^6.9.4",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
"sharp": "0.31.3",
|
"sharp": "0.31.3",
|
||||||
"ts-exif-parser": "0.2.2",
|
"ts-exif-parser": "0.2.2",
|
||||||
@ -16598,6 +16599,14 @@
|
|||||||
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
|
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/nodemailer": {
|
||||||
|
"version": "6.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.4.tgz",
|
||||||
|
"integrity": "sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/non-layered-tidy-tree-layout": {
|
"node_modules/non-layered-tidy-tree-layout": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz",
|
||||||
@ -35797,6 +35806,11 @@
|
|||||||
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
|
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"nodemailer": {
|
||||||
|
"version": "6.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.4.tgz",
|
||||||
|
"integrity": "sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA=="
|
||||||
|
},
|
||||||
"non-layered-tidy-tree-layout": {
|
"non-layered-tidy-tree-layout": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz",
|
||||||
|
@ -46,13 +46,14 @@
|
|||||||
"image-size": "1.0.2",
|
"image-size": "1.0.2",
|
||||||
"locale": "0.1.0",
|
"locale": "0.1.0",
|
||||||
"node-geocoder": "4.2.0",
|
"node-geocoder": "4.2.0",
|
||||||
|
"nodemailer": "^6.9.4",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
"sharp": "0.31.3",
|
"sharp": "0.31.3",
|
||||||
"ts-exif-parser": "0.2.2",
|
"ts-exif-parser": "0.2.2",
|
||||||
"ts-node-iptc": "1.0.11",
|
"ts-node-iptc": "1.0.11",
|
||||||
"typeconfig": "2.1.0",
|
"typeconfig": "2.1.0",
|
||||||
"xml2js": "0.4.23",
|
"typeorm": "0.3.12",
|
||||||
"typeorm": "0.3.12"
|
"xml2js": "0.4.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-builders/custom-webpack": "15.0.0",
|
"@angular-builders/custom-webpack": "15.0.0",
|
||||||
|
@ -3,9 +3,7 @@ import {promises as fsp} from 'fs';
|
|||||||
import * as archiver from 'archiver';
|
import * as archiver from 'archiver';
|
||||||
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 {
|
import {ParentDirectoryDTO,} from '../../common/entities/DirectoryDTO';
|
||||||
ParentDirectoryDTO,
|
|
||||||
} from '../../common/entities/DirectoryDTO';
|
|
||||||
import {ObjectManagers} from '../model/ObjectManagers';
|
import {ObjectManagers} from '../model/ObjectManagers';
|
||||||
import {ContentWrapper} from '../../common/entities/ConentWrapper';
|
import {ContentWrapper} from '../../common/entities/ConentWrapper';
|
||||||
import {ProjectPath} from '../ProjectPath';
|
import {ProjectPath} from '../ProjectPath';
|
||||||
@ -14,13 +12,11 @@ import {UserDTOUtils} from '../../common/entities/UserDTO';
|
|||||||
import {MediaDTO, MediaDTOUtils} from '../../common/entities/MediaDTO';
|
import {MediaDTO, MediaDTOUtils} from '../../common/entities/MediaDTO';
|
||||||
import {QueryParams} from '../../common/QueryParams';
|
import {QueryParams} from '../../common/QueryParams';
|
||||||
import {VideoProcessing} from '../model/fileprocessing/VideoProcessing';
|
import {VideoProcessing} from '../model/fileprocessing/VideoProcessing';
|
||||||
import {
|
import {SearchQueryDTO, SearchQueryTypes,} from '../../common/entities/SearchQueryDTO';
|
||||||
SearchQueryDTO,
|
|
||||||
SearchQueryTypes,
|
|
||||||
} from '../../common/entities/SearchQueryDTO';
|
|
||||||
import {LocationLookupException} from '../exceptions/LocationLookupException';
|
import {LocationLookupException} from '../exceptions/LocationLookupException';
|
||||||
import {SupportedFormats} from '../../common/SupportedFormats';
|
import {SupportedFormats} from '../../common/SupportedFormats';
|
||||||
import {ServerTime} from './ServerTimingMWs';
|
import {ServerTime} from './ServerTimingMWs';
|
||||||
|
import {SortingMethods} from '../../common/entities/SortingMethods';
|
||||||
|
|
||||||
export class GalleryMWs {
|
export class GalleryMWs {
|
||||||
@ServerTime('1.db', 'List Directory')
|
@ServerTime('1.db', 'List Directory')
|
||||||
@ -325,16 +321,16 @@ export class GalleryMWs {
|
|||||||
req.params['searchQueryDTO'] as string
|
req.params['searchQueryDTO'] as string
|
||||||
);
|
);
|
||||||
|
|
||||||
const photo =
|
const photos =
|
||||||
await ObjectManagers.getInstance().SearchManager.getRandomPhoto(query);
|
await ObjectManagers.getInstance().SearchManager.getNMedia(query, [SortingMethods.random], 1, true);
|
||||||
if (!photo) {
|
if (!photos || photos.length !== 1) {
|
||||||
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'No photo found'));
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'No photo found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
req.params['mediaPath'] = path.join(
|
req.params['mediaPath'] = path.join(
|
||||||
photo.directory.path,
|
photos[0].directory.path,
|
||||||
photo.directory.name,
|
photos[0].directory.name,
|
||||||
photo.name
|
photos[0].name
|
||||||
);
|
);
|
||||||
return next();
|
return next();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -30,11 +30,11 @@ import {
|
|||||||
} from '../../../common/entities/SearchQueryDTO';
|
} from '../../../common/entities/SearchQueryDTO';
|
||||||
import {GalleryManager} from './GalleryManager';
|
import {GalleryManager} from './GalleryManager';
|
||||||
import {ObjectManagers} from '../ObjectManagers';
|
import {ObjectManagers} from '../ObjectManagers';
|
||||||
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
|
||||||
import {DatabaseType} from '../../../common/config/private/PrivateConfig';
|
import {DatabaseType} from '../../../common/config/private/PrivateConfig';
|
||||||
import {Utils} from '../../../common/Utils';
|
import {Utils} from '../../../common/Utils';
|
||||||
import {FileEntity} from './enitites/FileEntity';
|
import {FileEntity} from './enitites/FileEntity';
|
||||||
import {SQL_COLLATE} from './enitites/EntityUtils';
|
import {SQL_COLLATE} from './enitites/EntityUtils';
|
||||||
|
import {SortingMethods} from '../../../common/entities/SortingMethods';
|
||||||
|
|
||||||
export class SearchManager {
|
export class SearchManager {
|
||||||
private DIRECTORY_SELECT = [
|
private DIRECTORY_SELECT = [
|
||||||
@ -348,19 +348,61 @@ export class SearchManager {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getRandomPhoto(query: SearchQueryDTO): Promise<PhotoDTO> {
|
|
||||||
|
private static setSorting<T>(
|
||||||
|
query: SelectQueryBuilder<T>,
|
||||||
|
sortings: SortingMethods[]
|
||||||
|
): SelectQueryBuilder<T> {
|
||||||
|
if (!sortings || !Array.isArray(sortings)) {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
if (sortings.includes(SortingMethods.random) && sortings.length > 0) {
|
||||||
|
throw new Error('Error during applying sorting: Can\' randomize and also sort the result. Bad input:' + sortings.map(s => SortingMethods[s]).join(', '));
|
||||||
|
}
|
||||||
|
for (const sort of sortings) {
|
||||||
|
switch (sort) {
|
||||||
|
case SortingMethods.descDate:
|
||||||
|
query.addOrderBy('media.metadata.creationDate', 'DESC');
|
||||||
|
break;
|
||||||
|
case SortingMethods.ascDate:
|
||||||
|
query.addOrderBy('media.metadata.creationDate', 'ASC');
|
||||||
|
break;
|
||||||
|
case SortingMethods.descRating:
|
||||||
|
query.addOrderBy('media.metadata.rating', 'DESC');
|
||||||
|
break;
|
||||||
|
case SortingMethods.ascRating:
|
||||||
|
query.addOrderBy('media.metadata.rating', 'ASC');
|
||||||
|
break;
|
||||||
|
case SortingMethods.descName:
|
||||||
|
query.addOrderBy('media.name', 'DESC');
|
||||||
|
break;
|
||||||
|
case SortingMethods.ascName:
|
||||||
|
query.addOrderBy('media.name', 'ASC');
|
||||||
|
break;
|
||||||
|
case SortingMethods.random:
|
||||||
|
if (Config.Database.type === DatabaseType.mysql) {
|
||||||
|
query.groupBy('RAND(), media.id');
|
||||||
|
}
|
||||||
|
query.groupBy('RANDOM()');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getNMedia(query: SearchQueryDTO, sortings: SortingMethods[], take: number, photoOnly = false) {
|
||||||
const connection = await SQLConnection.getConnection();
|
const connection = await SQLConnection.getConnection();
|
||||||
const sqlQuery: SelectQueryBuilder<PhotoEntity> = connection
|
const sqlQuery: SelectQueryBuilder<PhotoEntity> = connection
|
||||||
.getRepository(PhotoEntity)
|
.getRepository(photoOnly ? PhotoEntity : MediaEntity)
|
||||||
.createQueryBuilder('media')
|
.createQueryBuilder('media')
|
||||||
.select(['media', ...this.DIRECTORY_SELECT])
|
.select(['media', ...this.DIRECTORY_SELECT])
|
||||||
.innerJoin('media.directory', 'directory')
|
.innerJoin('media.directory', 'directory')
|
||||||
.where(await this.prepareAndBuildWhereQuery(query));
|
.where(await this.prepareAndBuildWhereQuery(query));
|
||||||
|
SearchManager.setSorting(sqlQuery, sortings);
|
||||||
|
|
||||||
|
return sqlQuery.limit(take).getMany();
|
||||||
|
|
||||||
if (Config.Database.type === DatabaseType.mysql) {
|
|
||||||
return await sqlQuery.groupBy('RAND(), media.id').limit(1).getOne();
|
|
||||||
}
|
|
||||||
return await sqlQuery.groupBy('RANDOM()').limit(1).getOne();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getCount(query: SearchQueryDTO): Promise<number> {
|
public async getCount(query: SearchQueryDTO): Promise<number> {
|
||||||
|
@ -10,6 +10,7 @@ import {PreviewRestJob} from './jobs/PreviewResetJob';
|
|||||||
import {GPXCompressionJob} from './jobs/GPXCompressionJob';
|
import {GPXCompressionJob} from './jobs/GPXCompressionJob';
|
||||||
import {AlbumRestJob} from './jobs/AlbumResetJob';
|
import {AlbumRestJob} from './jobs/AlbumResetJob';
|
||||||
import {GPXCompressionResetJob} from './jobs/GPXCompressionResetJob';
|
import {GPXCompressionResetJob} from './jobs/GPXCompressionResetJob';
|
||||||
|
import {TopPickSendJob} from './jobs/TopPickSendJob';
|
||||||
|
|
||||||
export class JobRepository {
|
export class JobRepository {
|
||||||
private static instance: JobRepository = null;
|
private static instance: JobRepository = null;
|
||||||
@ -45,3 +46,4 @@ JobRepository.Instance.register(new GPXCompressionJob());
|
|||||||
JobRepository.Instance.register(new TempFolderCleaningJob());
|
JobRepository.Instance.register(new TempFolderCleaningJob());
|
||||||
JobRepository.Instance.register(new AlbumRestJob());
|
JobRepository.Instance.register(new AlbumRestJob());
|
||||||
JobRepository.Instance.register(new GPXCompressionResetJob());
|
JobRepository.Instance.register(new GPXCompressionResetJob());
|
||||||
|
JobRepository.Instance.register(new TopPickSendJob());
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
import { ObjectManagers } from '../../ObjectManagers';
|
import {ObjectManagers} from '../../ObjectManagers';
|
||||||
import {
|
import {ConfigTemplateEntry, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
|
||||||
ConfigTemplateEntry,
|
import {Job} from './Job';
|
||||||
DefaultsJobs,
|
|
||||||
} from '../../../../common/entities/job/JobDTO';
|
|
||||||
import { Job } from './Job';
|
|
||||||
import { Config } from '../../../../common/config/private/Config';
|
|
||||||
import { DatabaseType } from '../../../../common/config/private/PrivateConfig';
|
|
||||||
|
|
||||||
export class PreviewFillingJob extends Job {
|
export class PreviewFillingJob extends Job {
|
||||||
public readonly Name = DefaultsJobs[DefaultsJobs['Preview Filling']];
|
public readonly Name = DefaultsJobs[DefaultsJobs['Preview Filling']];
|
||||||
@ -18,7 +13,7 @@ export class PreviewFillingJob extends Job {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async init(): Promise<void> {
|
protected async init(): Promise<void> {
|
||||||
// abstract function
|
this.status = 'Persons';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async step(): Promise<boolean> {
|
protected async step(): Promise<boolean> {
|
||||||
|
74
src/backend/model/jobs/jobs/TopPickSendJob.ts
Normal file
74
src/backend/model/jobs/jobs/TopPickSendJob.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import {ConfigTemplateEntry, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
|
||||||
|
import {Job} from './Job';
|
||||||
|
import {backendTexts} from '../../../../common/BackendTexts';
|
||||||
|
import {SortingMethods} from '../../../../common/entities/SortingMethods';
|
||||||
|
import {DatePatternFrequency, DatePatternSearch, SearchQueryDTO, SearchQueryTypes} from '../../../../common/entities/SearchQueryDTO';
|
||||||
|
import {ObjectManagers} from '../../ObjectManagers';
|
||||||
|
import {PhotoEntity} from '../../database/enitites/PhotoEntity';
|
||||||
|
|
||||||
|
|
||||||
|
export class TopPickSendJob extends Job<{ searchQuery: SearchQueryDTO, sortBy: SortingMethods[], pickAmount: number }> {
|
||||||
|
public readonly Name = DefaultsJobs[DefaultsJobs['Top Pick Sending']];
|
||||||
|
public readonly Supported: boolean = true;
|
||||||
|
public readonly ConfigTemplate: ConfigTemplateEntry[] = [
|
||||||
|
{
|
||||||
|
id: 'searchQuery',
|
||||||
|
type: 'SearchQuery',
|
||||||
|
name: backendTexts.searchQuery.name,
|
||||||
|
description: backendTexts.searchQuery.description,
|
||||||
|
defaultValue: {
|
||||||
|
type: SearchQueryTypes.date_pattern,
|
||||||
|
daysLength: 7,
|
||||||
|
frequency: DatePatternFrequency.every_year
|
||||||
|
} as DatePatternSearch,
|
||||||
|
}, {
|
||||||
|
id: 'sortby',
|
||||||
|
type: 'sort-array',
|
||||||
|
name: backendTexts.sortBy.name,
|
||||||
|
description: backendTexts.sortBy.description,
|
||||||
|
defaultValue: [SortingMethods.descRating],
|
||||||
|
}, {
|
||||||
|
id: 'pickAmount',
|
||||||
|
type: 'number',
|
||||||
|
name: backendTexts.pickAmount.name,
|
||||||
|
description: backendTexts.pickAmount.description,
|
||||||
|
defaultValue: 5,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
private status: 'Listing' | 'Sending' = 'Listing';
|
||||||
|
private mediaList: PhotoEntity[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
protected async init(): Promise<void> {
|
||||||
|
this.status = 'Listing';
|
||||||
|
this.mediaList = [];
|
||||||
|
this.Progress.Left = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected async step(): Promise<boolean> {
|
||||||
|
|
||||||
|
switch (this.status) {
|
||||||
|
case 'Listing':
|
||||||
|
if (!await this.stepListing()) {
|
||||||
|
this.status = 'Sending';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case 'Sending':
|
||||||
|
await this.stepSending();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async stepListing(): Promise<boolean> {
|
||||||
|
this.Progress.log('Collecting Photos and videos to Send');
|
||||||
|
this.Progress.Processed++;
|
||||||
|
this.mediaList = await ObjectManagers.getInstance().SearchManager.getNMedia(this.config.searchQuery, this.config.sortBy, this.config.pickAmount);
|
||||||
|
// console.log(this.mediaList);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async stepSending(): Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -3,4 +3,8 @@ export const backendTexts = {
|
|||||||
indexedFilesOnly: { name: 10, description: 12 },
|
indexedFilesOnly: { name: 10, description: 12 },
|
||||||
sizeToGenerate: { name: 20, description: 22 },
|
sizeToGenerate: { name: 20, description: 22 },
|
||||||
indexChangesOnly: { name: 30, description: 32 },
|
indexChangesOnly: { name: 30, description: 32 },
|
||||||
|
searchQuery: { name: 40, description: 42 },
|
||||||
|
sortBy: { name: 40, description: 42 },
|
||||||
|
pickAmount: { name: 40, description: 42 },
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {backendText} from '../../BackendTexts';
|
import {backendText} from '../../BackendTexts';
|
||||||
|
|
||||||
export type fieldType = 'string' | 'number' | 'boolean' | 'number-array';
|
export type fieldType = 'string' | 'number' | 'boolean' | 'number-array' | 'SearchQuery' | 'sort-array';
|
||||||
|
|
||||||
export enum DefaultsJobs {
|
export enum DefaultsJobs {
|
||||||
Indexing = 1,
|
Indexing = 1,
|
||||||
@ -13,7 +13,8 @@ export enum DefaultsJobs {
|
|||||||
'Preview Reset' = 8,
|
'Preview Reset' = 8,
|
||||||
'GPX Compression' = 9,
|
'GPX Compression' = 9,
|
||||||
'Album Reset' = 10,
|
'Album Reset' = 10,
|
||||||
'Delete Compressed GPX' = 11
|
'Delete Compressed GPX' = 11,
|
||||||
|
'Top Pick Sending' = 12
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigTemplateEntry {
|
export interface ConfigTemplateEntry {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user