mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-12-25 02:04:15 +02:00
implementing TempFolderCleaningJob
This commit is contained in:
parent
c3c94c1709
commit
e2864117b2
137
package-lock.json
generated
137
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pigallery2",
|
||||
"version": "1.7.8",
|
||||
"version": "1.7.9",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -4002,6 +4002,16 @@
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/rimraf": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.3.tgz",
|
||||
"integrity": "sha512-dZfyfL/u9l/oi984hEXdmAjX3JHry7TLWw43u1HQ8HhPv6KtfxnrZ3T/bleJ0GEvnk9t5sM7eePkgMqz3yBcGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/glob": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/selenium-webdriver": {
|
||||
"version": "3.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.14.tgz",
|
||||
@ -4472,12 +4482,6 @@
|
||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
|
||||
},
|
||||
"any-shell-escape": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/any-shell-escape/-/any-shell-escape-0.1.1.tgz",
|
||||
"integrity": "sha1-1Vq5ciRMcaml4asIefML8RCAaVk=",
|
||||
"dev": true
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
|
||||
@ -8725,15 +8729,6 @@
|
||||
"parse-filepath": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"first-chunk-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz",
|
||||
"integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"readable-stream": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"flagged-respawn": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
|
||||
@ -9990,90 +9985,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"gulp-git": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/gulp-git/-/gulp-git-2.10.0.tgz",
|
||||
"integrity": "sha512-AYh0xXpKdDYS+ftCuyF9+LFXoltjtFlpfKITTCKDI0LunztpwVuHFtp31SvRSFVZikvRHTHUGMZ9Z0TnXjDIxQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"any-shell-escape": "^0.1.1",
|
||||
"fancy-log": "^1.3.2",
|
||||
"lodash.template": "^4.4.0",
|
||||
"plugin-error": "^1.0.1",
|
||||
"require-dir": "^1.0.0",
|
||||
"strip-bom-stream": "^3.0.0",
|
||||
"through2": "^2.0.3",
|
||||
"vinyl": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-colors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
|
||||
"integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-wrap": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"clone-stats": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
|
||||
"integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.template": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
|
||||
"integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._reinterpolate": "^3.0.0",
|
||||
"lodash.templatesettings": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"lodash.templatesettings": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
|
||||
"integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._reinterpolate": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"plugin-error": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
|
||||
"integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-colors": "^1.0.1",
|
||||
"arr-diff": "^4.0.0",
|
||||
"arr-union": "^3.1.0",
|
||||
"extend-shallow": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"replace-ext": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
|
||||
"integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
|
||||
"dev": true
|
||||
},
|
||||
"vinyl": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz",
|
||||
"integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clone": "^2.1.1",
|
||||
"clone-buffer": "^1.0.0",
|
||||
"clone-stats": "^1.0.0",
|
||||
"cloneable-readable": "^1.0.0",
|
||||
"remove-trailing-separator": "^1.0.1",
|
||||
"replace-ext": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gulp-json-editor": {
|
||||
"version": "2.5.4",
|
||||
"resolved": "https://registry.npmjs.org/gulp-json-editor/-/gulp-json-editor-2.5.4.tgz",
|
||||
@ -15678,12 +15589,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"require-dir": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/require-dir/-/require-dir-1.2.0.tgz",
|
||||
"integrity": "sha512-LY85DTSu+heYgDqq/mK+7zFHWkttVNRXC9NKcKGyuGLdlsfbjEPrIEYdCVrx6hqnJb+xSu3Lzaoo8VnmOhhjNA==",
|
||||
"dev": true
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
@ -15788,7 +15693,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz",
|
||||
"integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
@ -17050,25 +16954,6 @@
|
||||
"is-utf8": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"strip-bom-buf": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz",
|
||||
"integrity": "sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-utf8": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"strip-bom-stream": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-3.0.0.tgz",
|
||||
"integrity": "sha1-lWvMXYRDD2klapDtgjdlzYWOFZw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"first-chunk-stream": "^2.0.0",
|
||||
"strip-bom-buf": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"strip-eof": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pigallery2",
|
||||
"version": "1.7.8",
|
||||
"version": "1.7.9",
|
||||
"description": "This is a photo gallery optimised for running low resource servers (especially on raspberry pi)",
|
||||
"author": "Patrik J. Braun",
|
||||
"homepage": "https://github.com/bpatrik/PiGallery2",
|
||||
@ -39,6 +39,7 @@
|
||||
"jimp": "0.9.3",
|
||||
"locale": "0.1.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rimraf": "^3.0.0",
|
||||
"sqlite3": "4.1.1",
|
||||
"ts-exif-parser": "0.1.4",
|
||||
"ts-node-iptc": "1.0.11",
|
||||
@ -75,6 +76,7 @@
|
||||
"@types/image-size": "0.8.0",
|
||||
"@types/jasmine": "3.5.0",
|
||||
"@types/node": "12.12.14",
|
||||
"@types/rimraf": "^2.0.3",
|
||||
"@types/sharp": "0.23.1",
|
||||
"@types/winston": "2.4.4",
|
||||
"@yaga/leaflet-ng2": "1.0.0",
|
||||
@ -135,6 +137,6 @@
|
||||
"sharp": "0.23.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10 <13.0"
|
||||
"node": ">=10.17 <13.0"
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import {Config} from '../common/config/private/Config';
|
||||
class ProjectPathClass {
|
||||
public Root: string;
|
||||
public ImageFolder: string;
|
||||
public ThumbnailFolder: string;
|
||||
public TempFolder: string;
|
||||
public TranscodedFolder: string;
|
||||
public FacesFolder: string;
|
||||
public FrontendFolder: string;
|
||||
@ -30,13 +30,13 @@ class ProjectPathClass {
|
||||
this.Root = path.join(__dirname, '/../../');
|
||||
this.FrontendFolder = path.join(this.Root, 'dist');
|
||||
this.ImageFolder = this.getAbsolutePath(Config.Server.Media.folder);
|
||||
this.ThumbnailFolder = this.getAbsolutePath(Config.Server.Media.tempFolder);
|
||||
this.TranscodedFolder = path.join(this.ThumbnailFolder, 'tc');
|
||||
this.FacesFolder = path.join(this.ThumbnailFolder, 'f');
|
||||
this.TempFolder = this.getAbsolutePath(Config.Server.Media.tempFolder);
|
||||
this.TranscodedFolder = path.join(this.TempFolder, 'tc');
|
||||
this.FacesFolder = path.join(this.TempFolder, 'f');
|
||||
|
||||
// create thumbnail folder if not exist
|
||||
if (!fs.existsSync(this.ThumbnailFolder)) {
|
||||
fs.mkdirSync(this.ThumbnailFolder);
|
||||
if (!fs.existsSync(this.TempFolder)) {
|
||||
fs.mkdirSync(this.TempFolder);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ export class GalleryMWs {
|
||||
}
|
||||
req.resultPipe = fullMediaPath;
|
||||
|
||||
const convertedVideo = VideoProcessing.generateConvertedFileName(fullMediaPath);
|
||||
const convertedVideo = VideoProcessing.generateConvertedFilePath(fullMediaPath);
|
||||
|
||||
// check if transcoded video exist
|
||||
if (fs.existsSync(convertedVideo) === true) {
|
||||
|
@ -145,7 +145,6 @@ export class ThumbnailGeneratorMWs {
|
||||
}
|
||||
|
||||
private static addThInfoToPhotos(photos: MediaDTO[]) {
|
||||
const thumbnailFolder = ProjectPath.ThumbnailFolder;
|
||||
for (let i = 0; i < photos.length; i++) {
|
||||
const fullMediaPath = path.join(ProjectPath.ImageFolder, photos[i].directory.path, photos[i].directory.name, photos[i].name);
|
||||
for (let j = 0; j < Config.Client.Media.Thumbnail.thumbnailSizes.length; j++) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import {constants as fsConstants, promises as fsp} from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as crypto from 'crypto';
|
||||
import {ProjectPath} from '../../ProjectPath';
|
||||
@ -9,6 +9,7 @@ import {PhotoWorker, RendererInput, ThumbnailSourceType} from '../threading/Phot
|
||||
import {ITaskExecuter, TaskExecuter} from '../threading/TaskExecuter';
|
||||
import {ServerConfig} from '../../../common/config/private/IPrivateConfig';
|
||||
import {FaceRegion, PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||
import {SupportedFormats} from '../../../common/SupportedFormats';
|
||||
|
||||
|
||||
export class PhotoProcessing {
|
||||
@ -60,8 +61,11 @@ export class PhotoProcessing {
|
||||
|
||||
|
||||
// check if thumbnail already exist
|
||||
if (fs.existsSync(thPath) === true) {
|
||||
try {
|
||||
await fsp.access(thPath, fsConstants.R_OK);
|
||||
return null;
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -95,11 +99,10 @@ export class PhotoProcessing {
|
||||
|
||||
|
||||
public static generateThumbnailPath(mediaPath: string, size: number): string {
|
||||
const extension = path.extname(mediaPath);
|
||||
const file = path.basename(mediaPath, extension);
|
||||
const file = path.basename(mediaPath);
|
||||
return path.join(ProjectPath.TranscodedFolder,
|
||||
ProjectPath.getRelativePathToImages(path.dirname(mediaPath)), file +
|
||||
'_' + size + '.jpg');
|
||||
ProjectPath.getRelativePathToImages(path.dirname(mediaPath)),
|
||||
file + '_' + size + '.jpg');
|
||||
}
|
||||
|
||||
public static generatePersonThumbnailPath(mediaPath: string, faceRegion: FaceRegion, size: number): string {
|
||||
@ -108,14 +111,34 @@ export class PhotoProcessing {
|
||||
.digest('hex') + '_' + size + '.jpg');
|
||||
}
|
||||
|
||||
|
||||
public static generateConvertedFilePath(photoPath: string): string {
|
||||
const extension = path.extname(photoPath);
|
||||
const file = path.basename(photoPath, extension);
|
||||
const postfix = Config.Server.Media.Photo.Converting.resolution;
|
||||
return path.join(ProjectPath.TranscodedFolder,
|
||||
ProjectPath.getRelativePathToImages(path.dirname(photoPath)), file +
|
||||
'_' + postfix + '.jpg');
|
||||
return this.generateThumbnailPath(photoPath, Config.Server.Media.Photo.Converting.resolution);
|
||||
}
|
||||
|
||||
public static async isValidConvertedPath(convertedPath: string): Promise<boolean> {
|
||||
const origFilePath = path.join(ProjectPath.ImageFolder,
|
||||
path.relative(ProjectPath.TranscodedFolder,
|
||||
convertedPath.substring(0, convertedPath.lastIndexOf('_'))));
|
||||
|
||||
const sizeStr = convertedPath.substring(convertedPath.lastIndexOf('_') + 1,
|
||||
convertedPath.length - path.extname(convertedPath).length);
|
||||
|
||||
const size = parseInt(sizeStr, 10);
|
||||
|
||||
if ((size + '').length !== sizeStr.length ||
|
||||
(Config.Client.Media.Thumbnail.thumbnailSizes.indexOf(size) === -1 &&
|
||||
Config.Server.Media.Photo.Converting.resolution !== size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await fsp.access(origFilePath, fsConstants.R_OK);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -125,8 +148,10 @@ export class PhotoProcessing {
|
||||
|
||||
|
||||
// check if file already exist
|
||||
if (fs.existsSync(outPath) === true) {
|
||||
try {
|
||||
await fsp.access(outPath, fsConstants.R_OK);
|
||||
return outPath;
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
|
||||
@ -141,9 +166,8 @@ export class PhotoProcessing {
|
||||
};
|
||||
|
||||
const outDir = path.dirname(input.outPath);
|
||||
if (!fs.existsSync(outDir)) {
|
||||
fs.mkdirSync(outDir, {recursive: true});
|
||||
}
|
||||
|
||||
await fsp.mkdir(outDir, {recursive: true});
|
||||
await this.taskQue.execute(input);
|
||||
return outPath;
|
||||
}
|
||||
@ -156,9 +180,11 @@ export class PhotoProcessing {
|
||||
const outPath = PhotoProcessing.generateThumbnailPath(mediaPath, size);
|
||||
|
||||
|
||||
// check if thumbnail already exist
|
||||
if (fs.existsSync(outPath) === true) {
|
||||
// check if file already exist
|
||||
try {
|
||||
await fsp.access(outPath, fsConstants.R_OK);
|
||||
return outPath;
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
|
||||
@ -173,11 +199,15 @@ export class PhotoProcessing {
|
||||
};
|
||||
|
||||
const outDir = path.dirname(input.outPath);
|
||||
if (!fs.existsSync(outDir)) {
|
||||
fs.mkdirSync(outDir, {recursive: true});
|
||||
}
|
||||
|
||||
await fsp.mkdir(outDir, {recursive: true});
|
||||
await this.taskQue.execute(input);
|
||||
return outPath;
|
||||
}
|
||||
|
||||
public static isPhoto(fullPath: string) {
|
||||
const extension = path.extname(fullPath).toLowerCase();
|
||||
return SupportedFormats.WithDots.Photos.indexOf(extension) !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,38 +1,56 @@
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import {ITaskExecuter, TaskExecuter} from '../../model/threading/TaskExecuter';
|
||||
import {VideoConverterInput, VideoConverterWorker} from '../../model/threading/VideoConverterWorker';
|
||||
import {MetadataLoader} from '../../model/threading/MetadataLoader';
|
||||
import {constants as fsConstants, promises as fsp} from 'fs';
|
||||
import {ITaskExecuter, TaskExecuter} from '../threading/TaskExecuter';
|
||||
import {VideoConverterInput, VideoConverterWorker} from '../threading/VideoConverterWorker';
|
||||
import {MetadataLoader} from '../threading/MetadataLoader';
|
||||
import {Config} from '../../../common/config/private/Config';
|
||||
import {ProjectPath} from '../../ProjectPath';
|
||||
import {SupportedFormats} from '../../../common/SupportedFormats';
|
||||
|
||||
const existPr = util.promisify(fs.exists);
|
||||
|
||||
export class VideoProcessing {
|
||||
private static taskQue: ITaskExecuter<VideoConverterInput, void> =
|
||||
new TaskExecuter(1, (input => VideoConverterWorker.convert(input)));
|
||||
|
||||
|
||||
public static generateConvertedFileName(videoPath: string): string {
|
||||
const extension = path.extname(videoPath);
|
||||
const file = path.basename(videoPath, extension);
|
||||
const postfix = Math.round(Config.Server.Media.Video.transcoding.bitRate / 1024) + 'k' +
|
||||
Config.Server.Media.Video.transcoding.codec.toString().toLowerCase() +
|
||||
Config.Server.Media.Video.transcoding.resolution;
|
||||
public static generateConvertedFilePath(videoPath: string): string {
|
||||
return path.join(ProjectPath.TranscodedFolder,
|
||||
ProjectPath.getRelativePathToImages(path.dirname(videoPath)), file +
|
||||
'_' + postfix + '.' + Config.Server.Media.Video.transcoding.format);
|
||||
ProjectPath.getRelativePathToImages(path.dirname(videoPath)),
|
||||
path.basename(videoPath) + '_' + this.getConvertedFilePostFix());
|
||||
}
|
||||
|
||||
public static async isValidConvertedPath(convertedPath: string): Promise<boolean> {
|
||||
|
||||
const origFilePath = path.join(ProjectPath.ImageFolder,
|
||||
path.relative(ProjectPath.TranscodedFolder,
|
||||
convertedPath.substring(0, convertedPath.lastIndexOf('_'))));
|
||||
|
||||
const postfix = convertedPath.substring(convertedPath.lastIndexOf('_') + 1, convertedPath.length);
|
||||
|
||||
if (postfix !== this.getConvertedFilePostFix()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await fsp.access(origFilePath, fsConstants.R_OK);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async convertVideo(videoPath: string): Promise<void> {
|
||||
|
||||
|
||||
const outPath = this.generateConvertedFileName(videoPath);
|
||||
const outPath = this.generateConvertedFilePath(videoPath);
|
||||
|
||||
if (await existPr(outPath)) {
|
||||
try {
|
||||
await fsp.access(outPath, fsConstants.R_OK);
|
||||
return;
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
const metaData = await MetadataLoader.loadVideoMetadata(videoPath);
|
||||
|
||||
const renderInput: VideoConverterInput = {
|
||||
@ -56,12 +74,23 @@ export class VideoProcessing {
|
||||
}
|
||||
|
||||
const outDir = path.dirname(renderInput.output.path);
|
||||
if (!fs.existsSync(outDir)) {
|
||||
fs.mkdirSync(outDir, {recursive: true});
|
||||
}
|
||||
|
||||
await fsp.mkdir(outDir, {recursive: true});
|
||||
await VideoProcessing.taskQue.execute(renderInput);
|
||||
|
||||
}
|
||||
|
||||
public static isVideo(fullPath: string) {
|
||||
const extension = path.extname(fullPath).toLowerCase();
|
||||
return SupportedFormats.WithDots.Videos.indexOf(extension) !== -1;
|
||||
}
|
||||
|
||||
protected static getConvertedFilePostFix(): string {
|
||||
return Math.round(Config.Server.Media.Video.transcoding.bitRate / 1024) + 'k' +
|
||||
Config.Server.Media.Video.transcoding.codec.toString().toLowerCase() +
|
||||
Config.Server.Media.Video.transcoding.resolution +
|
||||
'.' + Config.Server.Media.Video.transcoding.format.toLowerCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import {DBRestJob} from './jobs/DBResetJob';
|
||||
import {VideoConvertingJob} from './jobs/VideoConvertingJob';
|
||||
import {PhotoConvertingJob} from './jobs/PhotoConvertingJob';
|
||||
import {ThumbnailGenerationJob} from './jobs/ThumbnailGenerationJob';
|
||||
import {TempFolderCleaningJob} from './jobs/TempFolderCleaningJob';
|
||||
|
||||
export class JobRepository {
|
||||
|
||||
@ -21,7 +22,10 @@ export class JobRepository {
|
||||
return Object.values(this.availableJobs).filter(t => t.Supported);
|
||||
}
|
||||
|
||||
register(job: IJob<any>) {
|
||||
register(job: IJob<any>): void {
|
||||
if (typeof this.availableJobs[job.Name] !== 'undefined') {
|
||||
throw new Error('Job already exist:' + job.Name);
|
||||
}
|
||||
this.availableJobs[job.Name] = job;
|
||||
}
|
||||
}
|
||||
@ -32,3 +36,4 @@ JobRepository.Instance.register(new DBRestJob());
|
||||
JobRepository.Instance.register(new VideoConvertingJob());
|
||||
JobRepository.Instance.register(new PhotoConvertingJob());
|
||||
JobRepository.Instance.register(new ThumbnailGenerationJob());
|
||||
JobRepository.Instance.register(new TempFolderCleaningJob());
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {JobProgressDTO, JobState} from '../../../../common/entities/settings/JobProgressDTO';
|
||||
import {JobProgressDTO} from '../../../../common/entities/settings/JobProgressDTO';
|
||||
import {ConfigTemplateEntry} from '../../../../common/entities/job/JobDTO';
|
||||
import {Job} from './Job';
|
||||
import * as path from 'path';
|
||||
@ -6,7 +6,6 @@ import {DiskManager} from '../../DiskManger';
|
||||
import {DiskMangerWorker} from '../../threading/DiskMangerWorker';
|
||||
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||
import {Logger} from '../../../Logger';
|
||||
import {MediaDTO} from '../../../../common/entities/MediaDTO';
|
||||
|
||||
declare var global: NodeJS.Global;
|
||||
|
||||
@ -35,11 +34,7 @@ export abstract class FileJob<T, S = void> extends Job<S> {
|
||||
protected abstract async processFile(file: T): Promise<void>;
|
||||
|
||||
protected async step(): Promise<JobProgressDTO> {
|
||||
if ((this.directoryQueue.length === 0 && this.fileQueue.length === 0)
|
||||
|| this.state !== JobState.running) {
|
||||
if (global.gc) {
|
||||
global.gc();
|
||||
}
|
||||
if (this.directoryQueue.length === 0 && this.fileQueue.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -25,9 +25,6 @@ export class IndexingJob extends Job {
|
||||
|
||||
protected async step(): Promise<JobProgressDTO> {
|
||||
if (this.directoriesToIndex.length === 0) {
|
||||
if (global.gc) {
|
||||
global.gc();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const directory = this.directoriesToIndex.shift();
|
||||
|
@ -2,6 +2,7 @@ import {JobProgressDTO, JobState} from '../../../../common/entities/settings/Job
|
||||
import {Logger} from '../../../Logger';
|
||||
import {IJob} from './IJob';
|
||||
import {ConfigTemplateEntry, JobDTO} from '../../../../common/entities/job/JobDTO';
|
||||
import * as rimraf from 'rimraf';
|
||||
|
||||
declare const process: any;
|
||||
|
||||
@ -77,6 +78,9 @@ export abstract class Job<T = void> implements IJob<T> {
|
||||
|
||||
private onFinish(): void {
|
||||
this.progress = null;
|
||||
if (global.gc) {
|
||||
global.gc();
|
||||
}
|
||||
Logger.info(LOG_TAG, 'Job finished: ' + this.Name);
|
||||
if (this.IsInstant) {
|
||||
this.prResolve();
|
||||
@ -87,10 +91,13 @@ export abstract class Job<T = void> implements IJob<T> {
|
||||
process.nextTick(async () => {
|
||||
try {
|
||||
if (this.state === JobState.idle) {
|
||||
this.progress = null;
|
||||
return;
|
||||
}
|
||||
this.progress = await this.step();
|
||||
if (this.state === JobState.running) {
|
||||
this.progress = await this.step();
|
||||
} else {
|
||||
this.progress = null;
|
||||
}
|
||||
if (this.progress == null) { // finished
|
||||
this.state = JobState.idle;
|
||||
this.onFinish();
|
||||
|
118
src/backend/model/jobs/jobs/TempFolderCleaningJob.ts
Normal file
118
src/backend/model/jobs/jobs/TempFolderCleaningJob.ts
Normal file
@ -0,0 +1,118 @@
|
||||
import {ConfigTemplateEntry, DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
import {promises as fsp} from 'fs';
|
||||
import {Job} from './Job';
|
||||
import {JobProgressDTO} from '../../../../common/entities/settings/JobProgressDTO';
|
||||
import {ProjectPath} from '../../../ProjectPath';
|
||||
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
|
||||
import {VideoProcessing} from '../../fileprocessing/VideoProcessing';
|
||||
import * as rimraf from 'rimraf';
|
||||
|
||||
const LOG_TAG = '[TempFolderCleaningJob]';
|
||||
|
||||
const rimrafPR = util.promisify(rimraf);
|
||||
|
||||
|
||||
export class TempFolderCleaningJob extends Job {
|
||||
public readonly Name = DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']];
|
||||
public readonly ConfigTemplate: ConfigTemplateEntry[] = null;
|
||||
public readonly Supported = true;
|
||||
directoryQueue: string[] = [];
|
||||
private tempRootCleaned = false;
|
||||
|
||||
|
||||
protected async init() {
|
||||
this.tempRootCleaned = false;
|
||||
this.directoryQueue = [];
|
||||
this.directoryQueue.push(ProjectPath.TranscodedFolder);
|
||||
}
|
||||
|
||||
|
||||
protected async isValidFile(filePath: string): Promise<boolean> {
|
||||
if (PhotoProcessing.isPhoto(filePath)) {
|
||||
return PhotoProcessing.isValidConvertedPath(filePath);
|
||||
}
|
||||
|
||||
if (VideoProcessing.isVideo(filePath)) {
|
||||
return VideoProcessing.isValidConvertedPath(filePath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async isValidDirectory(filePath: string): Promise<boolean> {
|
||||
const originalPath = path.join(ProjectPath.ImageFolder,
|
||||
path.relative(ProjectPath.TranscodedFolder, filePath));
|
||||
try {
|
||||
await fsp.access(originalPath);
|
||||
return true;
|
||||
} catch (e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async readDir(dirPath: string): Promise<string[]> {
|
||||
return (await fsp.readdir(dirPath)).map(f => path.normalize(path.join(dirPath, f)));
|
||||
}
|
||||
|
||||
protected async stepTempDirectory() {
|
||||
const files = await this.readDir(ProjectPath.TempFolder);
|
||||
const validFiles = [ProjectPath.TranscodedFolder, ProjectPath.FacesFolder];
|
||||
for (let i = 0; i < files.length; ++i) {
|
||||
if (validFiles.indexOf(files[i]) === -1) {
|
||||
if ((await fsp.stat(files[i])).isDirectory()) {
|
||||
await rimrafPR(files[i]);
|
||||
} else {
|
||||
await fsp.unlink(files[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.progress.time.current = Date.now();
|
||||
|
||||
this.progress.comment = 'processing: ' + ProjectPath.TempFolder;
|
||||
|
||||
return this.progress;
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected async stepConvertedDirectory() {
|
||||
|
||||
|
||||
this.progress.time.current = Date.now();
|
||||
|
||||
|
||||
const filePath = this.directoryQueue.shift();
|
||||
const stat = await fsp.stat(filePath);
|
||||
|
||||
this.progress.left = this.directoryQueue.length;
|
||||
this.progress.progress++;
|
||||
this.progress.comment = 'processing: ' + filePath;
|
||||
if (stat.isDirectory()) {
|
||||
if (await this.isValidDirectory(filePath) === false) {
|
||||
await rimrafPR(filePath);
|
||||
} else {
|
||||
this.directoryQueue = this.directoryQueue.concat(await this.readDir(filePath));
|
||||
}
|
||||
} else {
|
||||
if (await this.isValidFile(filePath) === false) {
|
||||
await fsp.unlink(filePath);
|
||||
}
|
||||
}
|
||||
return this.progress;
|
||||
}
|
||||
|
||||
protected async step(): Promise<JobProgressDTO> {
|
||||
if (this.directoryQueue.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (this.tempRootCleaned === false) {
|
||||
this.tempRootCleaned = true;
|
||||
return this.stepTempDirectory();
|
||||
}
|
||||
return this.stepConvertedDirectory();
|
||||
}
|
||||
|
||||
}
|
@ -2,8 +2,6 @@ import {Config} from '../../../../common/config/private/Config';
|
||||
import {ConfigTemplateEntry, DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
||||
import {ProjectPath} from '../../../ProjectPath';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import {FileJob} from './FileJob';
|
||||
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
|
||||
@ -11,7 +9,6 @@ import {ThumbnailSourceType} from '../../threading/PhotoWorker';
|
||||
import {MediaDTO} from '../../../../common/entities/MediaDTO';
|
||||
|
||||
const LOG_TAG = '[ThumbnailGenerationJob]';
|
||||
const existsPr = util.promisify(fs.exists);
|
||||
|
||||
|
||||
export class ThumbnailGenerationJob extends FileJob<MediaDTO, { sizes: number[] }> {
|
||||
|
@ -31,7 +31,7 @@ export class VideoConvertingJob extends FileJob<string> {
|
||||
directory.media[i].directory.name,
|
||||
directory.media[i].name);
|
||||
|
||||
if (await existsPr(VideoProcessing.generateConvertedFileName(videoPath)) === false) {
|
||||
if (await existsPr(VideoProcessing.generateConvertedFilePath(videoPath)) === false) {
|
||||
ret.push(videoPath);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as fs from 'fs';
|
||||
import {Stats} from 'fs';
|
||||
import {promises as fsp, Stats} from 'fs';
|
||||
import * as path from 'path';
|
||||
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
||||
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||
@ -10,6 +9,8 @@ import {FileDTO} from '../../../common/entities/FileDTO';
|
||||
import {MetadataLoader} from './MetadataLoader';
|
||||
import {Logger} from '../../Logger';
|
||||
import {SupportedFormats} from '../../../common/SupportedFormats';
|
||||
import {VideoProcessing} from '../fileprocessing/VideoProcessing';
|
||||
import {PhotoProcessing} from '../fileprocessing/PhotoProcessing';
|
||||
|
||||
const LOG_TAG = '[DiskMangerWorker]';
|
||||
|
||||
@ -41,7 +42,7 @@ export class DiskMangerWorker {
|
||||
return path.basename(name);
|
||||
}
|
||||
|
||||
public static excludeDir(name: string, relativeDirectoryName: string, absoluteDirectoryName: string) {
|
||||
public static async excludeDir(name: string, relativeDirectoryName: string, absoluteDirectoryName: string) {
|
||||
if (Config.Server.Indexing.excludeFolderList.length === 0 ||
|
||||
Config.Server.Indexing.excludeFileList.length === 0) {
|
||||
return false;
|
||||
@ -70,122 +71,105 @@ export class DiskMangerWorker {
|
||||
for (let j = 0; j < Config.Server.Indexing.excludeFileList.length; j++) {
|
||||
const exclude = Config.Server.Indexing.excludeFileList[j];
|
||||
|
||||
if (fs.existsSync(path.join(absoluteName, exclude))) {
|
||||
try {
|
||||
await fsp.access(path.join(absoluteName, exclude));
|
||||
return true;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static scanDirectory(relativeDirectoryName: string, settings: DiskMangerWorker.DirectoryScanSettings = {}): Promise<DirectoryDTO> {
|
||||
return new Promise<DirectoryDTO>((resolve, reject) => {
|
||||
relativeDirectoryName = this.normalizeDirPath(relativeDirectoryName);
|
||||
const directoryName = DiskMangerWorker.dirName(relativeDirectoryName);
|
||||
const directoryParent = this.pathFromRelativeDirName(relativeDirectoryName);
|
||||
const absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName);
|
||||
public static async scanDirectory(relativeDirectoryName: string,
|
||||
settings: DiskMangerWorker.DirectoryScanSettings = {}): Promise<DirectoryDTO> {
|
||||
|
||||
const stat = fs.statSync(path.join(ProjectPath.ImageFolder, relativeDirectoryName));
|
||||
const directory: DirectoryDTO = {
|
||||
id: null,
|
||||
parent: null,
|
||||
name: directoryName,
|
||||
path: directoryParent,
|
||||
lastModified: this.calcLastModified(stat),
|
||||
lastScanned: Date.now(),
|
||||
directories: [],
|
||||
isPartial: false,
|
||||
mediaCount: 0,
|
||||
media: [],
|
||||
metaFile: []
|
||||
};
|
||||
fs.readdir(absoluteDirectoryName, async (err, list: string[]) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
relativeDirectoryName = this.normalizeDirPath(relativeDirectoryName);
|
||||
const directoryName = DiskMangerWorker.dirName(relativeDirectoryName);
|
||||
const directoryParent = this.pathFromRelativeDirName(relativeDirectoryName);
|
||||
const absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName);
|
||||
|
||||
const stat = await fsp.stat(path.join(ProjectPath.ImageFolder, relativeDirectoryName));
|
||||
const directory: DirectoryDTO = {
|
||||
id: null,
|
||||
parent: null,
|
||||
name: directoryName,
|
||||
path: directoryParent,
|
||||
lastModified: this.calcLastModified(stat),
|
||||
lastScanned: Date.now(),
|
||||
directories: [],
|
||||
isPartial: false,
|
||||
mediaCount: 0,
|
||||
media: [],
|
||||
metaFile: []
|
||||
};
|
||||
const list = await fsp.readdir(absoluteDirectoryName);
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const file = list[i];
|
||||
const fullFilePath = path.normalize(path.join(absoluteDirectoryName, file));
|
||||
if ((await fsp.stat(fullFilePath)).isDirectory()) {
|
||||
if (settings.noDirectory === true ||
|
||||
await DiskMangerWorker.excludeDir(file, relativeDirectoryName, absoluteDirectoryName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create preview directory
|
||||
const d = await DiskMangerWorker.scanDirectory(path.join(relativeDirectoryName, file),
|
||||
{
|
||||
maxPhotos: Config.Server.Indexing.folderPreviewSize,
|
||||
noMetaFile: true,
|
||||
noVideo: true,
|
||||
noDirectory: true
|
||||
}
|
||||
);
|
||||
d.lastScanned = 0; // it was not a fully scan
|
||||
d.isPartial = true;
|
||||
directory.directories.push(d);
|
||||
} else if (PhotoProcessing.isPhoto(fullFilePath)) {
|
||||
if (settings.noPhoto) {
|
||||
continue;
|
||||
}
|
||||
directory.media.push(<PhotoDTO>{
|
||||
name: file,
|
||||
directory: null,
|
||||
metadata: await MetadataLoader.loadPhotoMetadata(fullFilePath)
|
||||
});
|
||||
|
||||
if (settings.maxPhotos && directory.media.length > settings.maxPhotos) {
|
||||
break;
|
||||
}
|
||||
} else if (VideoProcessing.isVideo(fullFilePath)) {
|
||||
if (Config.Client.Media.Video.enabled === false || settings.noVideo) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const file = list[i];
|
||||
const fullFilePath = path.normalize(path.join(absoluteDirectoryName, file));
|
||||
if (fs.statSync(fullFilePath).isDirectory()) {
|
||||
if (settings.noDirectory === true ||
|
||||
DiskMangerWorker.excludeDir(file, relativeDirectoryName, absoluteDirectoryName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create preview directory
|
||||
const d = await DiskMangerWorker.scanDirectory(path.join(relativeDirectoryName, file),
|
||||
{
|
||||
maxPhotos: Config.Server.Indexing.folderPreviewSize,
|
||||
noMetaFile: true,
|
||||
noVideo: true,
|
||||
noDirectory: true
|
||||
}
|
||||
);
|
||||
d.lastScanned = 0; // it was not a fully scan
|
||||
d.isPartial = true;
|
||||
directory.directories.push(d);
|
||||
} else if (DiskMangerWorker.isImage(fullFilePath)) {
|
||||
if (settings.noPhoto) {
|
||||
continue;
|
||||
}
|
||||
directory.media.push(<PhotoDTO>{
|
||||
name: file,
|
||||
directory: null,
|
||||
metadata: await MetadataLoader.loadPhotoMetadata(fullFilePath)
|
||||
});
|
||||
|
||||
if (settings.maxPhotos && directory.media.length > settings.maxPhotos) {
|
||||
break;
|
||||
}
|
||||
} else if (DiskMangerWorker.isVideo(fullFilePath)) {
|
||||
if (Config.Client.Media.Video.enabled === false || settings.noVideo) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
directory.media.push(<VideoDTO>{
|
||||
name: file,
|
||||
directory: null,
|
||||
metadata: await MetadataLoader.loadVideoMetadata(fullFilePath)
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.warn('Media loading error, skipping: ' + file + ', reason: ' + e.toString());
|
||||
}
|
||||
|
||||
} else if (DiskMangerWorker.isMetaFile(fullFilePath)) {
|
||||
if (Config.Client.MetaFile.enabled === false || settings.noMetaFile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
directory.metaFile.push(<FileDTO>{
|
||||
name: file,
|
||||
directory: null,
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
directory.mediaCount = directory.media.length;
|
||||
|
||||
return resolve(directory);
|
||||
} catch (err) {
|
||||
return reject({error: err});
|
||||
directory.media.push(<VideoDTO>{
|
||||
name: file,
|
||||
directory: null,
|
||||
metadata: await MetadataLoader.loadVideoMetadata(fullFilePath)
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.warn('Media loading error, skipping: ' + file + ', reason: ' + e.toString());
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
} else if (DiskMangerWorker.isMetaFile(fullFilePath)) {
|
||||
if (Config.Client.MetaFile.enabled === false || settings.noMetaFile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
directory.metaFile.push(<FileDTO>{
|
||||
name: file,
|
||||
directory: null,
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
directory.mediaCount = directory.media.length;
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
private static isImage(fullPath: string) {
|
||||
const extension = path.extname(fullPath).toLowerCase();
|
||||
return SupportedFormats.WithDots.Photos.indexOf(extension) !== -1;
|
||||
}
|
||||
|
||||
private static isVideo(fullPath: string) {
|
||||
const extension = path.extname(fullPath).toLowerCase();
|
||||
return SupportedFormats.WithDots.Videos.indexOf(extension) !== -1;
|
||||
}
|
||||
|
||||
private static isMetaFile(fullPath: string) {
|
||||
const extension = path.extname(fullPath).toLowerCase();
|
||||
|
@ -2,7 +2,12 @@ export type fieldType = 'string' | 'number' | 'boolean' | 'number-array';
|
||||
|
||||
|
||||
export enum DefaultsJobs {
|
||||
Indexing = 1, 'Database Reset' = 2, 'Video Converting' = 3, 'Photo Converting' = 4, 'Thumbnail Generation' = 5
|
||||
Indexing = 1,
|
||||
'Database Reset' = 2,
|
||||
'Video Converting' = 3,
|
||||
'Photo Converting' = 4,
|
||||
'Thumbnail Generation' = 5,
|
||||
'Temp Folder Cleaning' = 6
|
||||
}
|
||||
|
||||
export interface ConfigTemplateEntry {
|
||||
|
@ -8,11 +8,11 @@ import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||
import {ErrorDTO} from '../../../../../common/entities/Error';
|
||||
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
||||
import {
|
||||
JobScheduleDTO,
|
||||
JobTriggerType,
|
||||
NeverJobTrigger,
|
||||
PeriodicJobTrigger,
|
||||
ScheduledJobTrigger,
|
||||
JobScheduleDTO,
|
||||
JobTriggerType
|
||||
ScheduledJobTrigger
|
||||
} from '../../../../../common/entities/job/JobScheduleDTO';
|
||||
import {Utils} from '../../../../../common/Utils';
|
||||
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
||||
@ -64,7 +64,6 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
||||
}
|
||||
|
||||
|
||||
|
||||
getConfigTemplate(JobName: string): ConfigTemplateEntry[] {
|
||||
const job = this._settingsService.availableJobs.value.find(t => t.Name === JobName);
|
||||
if (job && job.ConfigTemplate && job.ConfigTemplate.length > 0) {
|
||||
@ -131,7 +130,9 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
||||
jobTypeChanged(schedule: JobScheduleDTO) {
|
||||
const job = this._settingsService.availableJobs.value.find(t => t.Name === schedule.jobName);
|
||||
schedule.config = schedule.config || {};
|
||||
job.ConfigTemplate.forEach(ct => schedule.config[ct.id] = ct.defaultValue);
|
||||
if (job.ConfigTemplate) {
|
||||
job.ConfigTemplate.forEach(ct => schedule.config[ct.id] = ct.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
addNewJob() {
|
||||
@ -146,7 +147,9 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
||||
|
||||
const job = this._settingsService.availableJobs.value.find(t => t.Name === jobName);
|
||||
newSchedule.config = newSchedule.config || {};
|
||||
job.ConfigTemplate.forEach(ct => newSchedule.config[ct.id] = ct.defaultValue);
|
||||
if (job.ConfigTemplate) {
|
||||
job.ConfigTemplate.forEach(ct => newSchedule.config[ct.id] = ct.defaultValue);
|
||||
}
|
||||
this.settings.scheduled.push(newSchedule);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,61 @@
|
||||
import {expect} from 'chai';
|
||||
import {Config} from '../../../../../src/common/config/private/Config';
|
||||
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
||||
import * as path from 'path';
|
||||
import {PhotoProcessing} from '../../../../../src/backend/model/fileprocessing/PhotoProcessing';
|
||||
|
||||
|
||||
describe('PhotoProcessing', () => {
|
||||
|
||||
// tslint:disable:no-unused-expression
|
||||
it('should generate converted file path', async () => {
|
||||
|
||||
Config.load();
|
||||
Config.Client.Media.Thumbnail.thumbnailSizes = [];
|
||||
ProjectPath.ImageFolder = path.join(__dirname, './../../assets');
|
||||
const photoPath = path.join(ProjectPath.ImageFolder, 'test_png.png');
|
||||
|
||||
expect(await PhotoProcessing
|
||||
.isValidConvertedPath(PhotoProcessing.generateConvertedFilePath(photoPath)))
|
||||
.to.be.true;
|
||||
|
||||
expect(await PhotoProcessing
|
||||
.isValidConvertedPath(PhotoProcessing.generateConvertedFilePath(photoPath + 'noPath')))
|
||||
.to.be.false;
|
||||
|
||||
{
|
||||
const convertedPath = PhotoProcessing.generateConvertedFilePath(photoPath);
|
||||
Config.Server.Media.Photo.Converting.resolution = <any>1;
|
||||
expect(await PhotoProcessing.isValidConvertedPath(convertedPath)).to.be.false;
|
||||
}
|
||||
});
|
||||
|
||||
// tslint:disable:no-unused-expression
|
||||
it('should generate converted thumbnail path', async () => {
|
||||
|
||||
Config.load();
|
||||
Config.Server.Media.Photo.Converting.resolution = <any>null;
|
||||
Config.Client.Media.Thumbnail.thumbnailSizes = [10, 20];
|
||||
ProjectPath.ImageFolder = path.join(__dirname, './../../assets');
|
||||
const photoPath = path.join(ProjectPath.ImageFolder, 'test_png.png');
|
||||
|
||||
for (let i = 0; i < Config.Client.Media.Thumbnail.thumbnailSizes.length; ++i) {
|
||||
const thSize = Config.Client.Media.Thumbnail.thumbnailSizes[i];
|
||||
expect(await PhotoProcessing
|
||||
.isValidConvertedPath(PhotoProcessing.generateThumbnailPath(photoPath, thSize)))
|
||||
.to.be.true;
|
||||
|
||||
|
||||
expect(await PhotoProcessing
|
||||
.isValidConvertedPath(PhotoProcessing.generateThumbnailPath(photoPath + 'noPath', thSize)))
|
||||
.to.be.false;
|
||||
}
|
||||
|
||||
|
||||
expect(await PhotoProcessing
|
||||
.isValidConvertedPath(PhotoProcessing.generateThumbnailPath(photoPath, 30)))
|
||||
.to.be.false;
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
import {expect} from 'chai';
|
||||
import {VideoProcessing} from '../../../../../src/backend/model/fileprocessing/VideoProcessing';
|
||||
import {Config} from '../../../../../src/common/config/private/Config';
|
||||
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
||||
import * as path from 'path';
|
||||
|
||||
|
||||
describe('VideoProcessing', () => {
|
||||
|
||||
// tslint:disable:no-unused-expression
|
||||
it('should generate converted file path', async () => {
|
||||
|
||||
ProjectPath.ImageFolder = path.join(__dirname, './../../assets');
|
||||
const videoPath = path.join(ProjectPath.ImageFolder, 'video.mp4');
|
||||
expect(await VideoProcessing
|
||||
.isValidConvertedPath(VideoProcessing.generateConvertedFilePath(videoPath)))
|
||||
.to.be.true;
|
||||
|
||||
expect(await VideoProcessing
|
||||
.isValidConvertedPath(VideoProcessing.generateConvertedFilePath(videoPath + 'noPath')))
|
||||
.to.be.false;
|
||||
|
||||
{
|
||||
const convertedPath = VideoProcessing.generateConvertedFilePath(videoPath);
|
||||
Config.Server.Media.Video.transcoding.bitRate = 10;
|
||||
expect(await VideoProcessing.isValidConvertedPath(convertedPath)).to.be.false;
|
||||
}
|
||||
{
|
||||
const convertedPath = VideoProcessing.generateConvertedFilePath(videoPath);
|
||||
Config.Server.Media.Video.transcoding.codec = <any>'codec_text';
|
||||
expect(await VideoProcessing.isValidConvertedPath(convertedPath)).to.be.false;
|
||||
}
|
||||
{
|
||||
const convertedPath = VideoProcessing.generateConvertedFilePath(videoPath);
|
||||
Config.Server.Media.Video.transcoding.format = <any>'format_test';
|
||||
expect(await VideoProcessing.isValidConvertedPath(convertedPath)).to.be.false;
|
||||
}
|
||||
{
|
||||
const convertedPath = VideoProcessing.generateConvertedFilePath(videoPath);
|
||||
Config.Server.Media.Video.transcoding.resolution = <any>1;
|
||||
expect(await VideoProcessing.isValidConvertedPath(convertedPath)).to.be.false;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user