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",
|
||||
"locale": "0.1.0",
|
||||
"node-geocoder": "4.2.0",
|
||||
"nodemailer": "^6.9.4",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"sharp": "0.31.3",
|
||||
"ts-exif-parser": "0.2.2",
|
||||
@ -16598,6 +16599,14 @@
|
||||
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
|
||||
"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": {
|
||||
"version": "2.0.2",
|
||||
"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==",
|
||||
"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": {
|
||||
"version": "2.0.2",
|
||||
"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",
|
||||
"locale": "0.1.0",
|
||||
"node-geocoder": "4.2.0",
|
||||
"nodemailer": "^6.9.4",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"sharp": "0.31.3",
|
||||
"ts-exif-parser": "0.2.2",
|
||||
"ts-node-iptc": "1.0.11",
|
||||
"typeconfig": "2.1.0",
|
||||
"xml2js": "0.4.23",
|
||||
"typeorm": "0.3.12"
|
||||
"typeorm": "0.3.12",
|
||||
"xml2js": "0.4.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "15.0.0",
|
||||
|
@ -3,9 +3,7 @@ import {promises as fsp} from 'fs';
|
||||
import * as archiver from 'archiver';
|
||||
import {NextFunction, Request, Response} from 'express';
|
||||
import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
|
||||
import {
|
||||
ParentDirectoryDTO,
|
||||
} from '../../common/entities/DirectoryDTO';
|
||||
import {ParentDirectoryDTO,} from '../../common/entities/DirectoryDTO';
|
||||
import {ObjectManagers} from '../model/ObjectManagers';
|
||||
import {ContentWrapper} from '../../common/entities/ConentWrapper';
|
||||
import {ProjectPath} from '../ProjectPath';
|
||||
@ -14,13 +12,11 @@ import {UserDTOUtils} from '../../common/entities/UserDTO';
|
||||
import {MediaDTO, MediaDTOUtils} from '../../common/entities/MediaDTO';
|
||||
import {QueryParams} from '../../common/QueryParams';
|
||||
import {VideoProcessing} from '../model/fileprocessing/VideoProcessing';
|
||||
import {
|
||||
SearchQueryDTO,
|
||||
SearchQueryTypes,
|
||||
} from '../../common/entities/SearchQueryDTO';
|
||||
import {SearchQueryDTO, SearchQueryTypes,} from '../../common/entities/SearchQueryDTO';
|
||||
import {LocationLookupException} from '../exceptions/LocationLookupException';
|
||||
import {SupportedFormats} from '../../common/SupportedFormats';
|
||||
import {ServerTime} from './ServerTimingMWs';
|
||||
import {SortingMethods} from '../../common/entities/SortingMethods';
|
||||
|
||||
export class GalleryMWs {
|
||||
@ServerTime('1.db', 'List Directory')
|
||||
@ -325,16 +321,16 @@ export class GalleryMWs {
|
||||
req.params['searchQueryDTO'] as string
|
||||
);
|
||||
|
||||
const photo =
|
||||
await ObjectManagers.getInstance().SearchManager.getRandomPhoto(query);
|
||||
if (!photo) {
|
||||
const photos =
|
||||
await ObjectManagers.getInstance().SearchManager.getNMedia(query, [SortingMethods.random], 1, true);
|
||||
if (!photos || photos.length !== 1) {
|
||||
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'No photo found'));
|
||||
}
|
||||
|
||||
req.params['mediaPath'] = path.join(
|
||||
photo.directory.path,
|
||||
photo.directory.name,
|
||||
photo.name
|
||||
photos[0].directory.path,
|
||||
photos[0].directory.name,
|
||||
photos[0].name
|
||||
);
|
||||
return next();
|
||||
} catch (e) {
|
||||
|
@ -30,11 +30,11 @@ import {
|
||||
} from '../../../common/entities/SearchQueryDTO';
|
||||
import {GalleryManager} from './GalleryManager';
|
||||
import {ObjectManagers} from '../ObjectManagers';
|
||||
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||
import {DatabaseType} from '../../../common/config/private/PrivateConfig';
|
||||
import {Utils} from '../../../common/Utils';
|
||||
import {FileEntity} from './enitites/FileEntity';
|
||||
import {SQL_COLLATE} from './enitites/EntityUtils';
|
||||
import {SortingMethods} from '../../../common/entities/SortingMethods';
|
||||
|
||||
export class SearchManager {
|
||||
private DIRECTORY_SELECT = [
|
||||
@ -348,19 +348,61 @@ export class SearchManager {
|
||||
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 sqlQuery: SelectQueryBuilder<PhotoEntity> = connection
|
||||
.getRepository(PhotoEntity)
|
||||
.getRepository(photoOnly ? PhotoEntity : MediaEntity)
|
||||
.createQueryBuilder('media')
|
||||
.select(['media', ...this.DIRECTORY_SELECT])
|
||||
.innerJoin('media.directory', 'directory')
|
||||
.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> {
|
||||
|
@ -10,6 +10,7 @@ import {PreviewRestJob} from './jobs/PreviewResetJob';
|
||||
import {GPXCompressionJob} from './jobs/GPXCompressionJob';
|
||||
import {AlbumRestJob} from './jobs/AlbumResetJob';
|
||||
import {GPXCompressionResetJob} from './jobs/GPXCompressionResetJob';
|
||||
import {TopPickSendJob} from './jobs/TopPickSendJob';
|
||||
|
||||
export class JobRepository {
|
||||
private static instance: JobRepository = null;
|
||||
@ -45,3 +46,4 @@ JobRepository.Instance.register(new GPXCompressionJob());
|
||||
JobRepository.Instance.register(new TempFolderCleaningJob());
|
||||
JobRepository.Instance.register(new AlbumRestJob());
|
||||
JobRepository.Instance.register(new GPXCompressionResetJob());
|
||||
JobRepository.Instance.register(new TopPickSendJob());
|
||||
|
@ -1,11 +1,6 @@
|
||||
import { ObjectManagers } from '../../ObjectManagers';
|
||||
import {
|
||||
ConfigTemplateEntry,
|
||||
DefaultsJobs,
|
||||
} from '../../../../common/entities/job/JobDTO';
|
||||
import { Job } from './Job';
|
||||
import { Config } from '../../../../common/config/private/Config';
|
||||
import { DatabaseType } from '../../../../common/config/private/PrivateConfig';
|
||||
import {ObjectManagers} from '../../ObjectManagers';
|
||||
import {ConfigTemplateEntry, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
|
||||
import {Job} from './Job';
|
||||
|
||||
export class PreviewFillingJob extends Job {
|
||||
public readonly Name = DefaultsJobs[DefaultsJobs['Preview Filling']];
|
||||
@ -18,7 +13,7 @@ export class PreviewFillingJob extends Job {
|
||||
}
|
||||
|
||||
protected async init(): Promise<void> {
|
||||
// abstract function
|
||||
this.status = 'Persons';
|
||||
}
|
||||
|
||||
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 },
|
||||
sizeToGenerate: { name: 20, description: 22 },
|
||||
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';
|
||||
|
||||
export type fieldType = 'string' | 'number' | 'boolean' | 'number-array';
|
||||
export type fieldType = 'string' | 'number' | 'boolean' | 'number-array' | 'SearchQuery' | 'sort-array';
|
||||
|
||||
export enum DefaultsJobs {
|
||||
Indexing = 1,
|
||||
@ -13,7 +13,8 @@ export enum DefaultsJobs {
|
||||
'Preview Reset' = 8,
|
||||
'GPX Compression' = 9,
|
||||
'Album Reset' = 10,
|
||||
'Delete Compressed GPX' = 11
|
||||
'Delete Compressed GPX' = 11,
|
||||
'Top Pick Sending' = 12
|
||||
}
|
||||
|
||||
export interface ConfigTemplateEntry {
|
||||
|
Loading…
x
Reference in New Issue
Block a user