mirror of
https://github.com/immich-app/immich.git
synced 2025-01-04 02:46:47 +01:00
refactor(server): system config (#9484)
This commit is contained in:
parent
42d0fc85ca
commit
7b1112f3e3
24 changed files with 379 additions and 397 deletions
|
@ -1,12 +1,344 @@
|
||||||
import { RegisterQueueOptions } from '@nestjs/bullmq';
|
import { RegisterQueueOptions } from '@nestjs/bullmq';
|
||||||
import { ConfigModuleOptions } from '@nestjs/config';
|
import { ConfigModuleOptions } from '@nestjs/config';
|
||||||
|
import { CronExpression } from '@nestjs/schedule';
|
||||||
import { QueueOptions } from 'bullmq';
|
import { QueueOptions } from 'bullmq';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { RedisOptions } from 'ioredis';
|
import { RedisOptions } from 'ioredis';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
import { CLS_ID, ClsModuleOptions } from 'nestjs-cls';
|
import { CLS_ID, ClsModuleOptions } from 'nestjs-cls';
|
||||||
import { LogLevel } from 'src/entities/system-config.entity';
|
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { QueueName } from 'src/interfaces/job.interface';
|
|
||||||
|
export enum TranscodePolicy {
|
||||||
|
ALL = 'all',
|
||||||
|
OPTIMAL = 'optimal',
|
||||||
|
BITRATE = 'bitrate',
|
||||||
|
REQUIRED = 'required',
|
||||||
|
DISABLED = 'disabled',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TranscodeTarget {
|
||||||
|
NONE,
|
||||||
|
AUDIO,
|
||||||
|
VIDEO,
|
||||||
|
ALL,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VideoCodec {
|
||||||
|
H264 = 'h264',
|
||||||
|
HEVC = 'hevc',
|
||||||
|
VP9 = 'vp9',
|
||||||
|
AV1 = 'av1',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AudioCodec {
|
||||||
|
MP3 = 'mp3',
|
||||||
|
AAC = 'aac',
|
||||||
|
LIBOPUS = 'libopus',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TranscodeHWAccel {
|
||||||
|
NVENC = 'nvenc',
|
||||||
|
QSV = 'qsv',
|
||||||
|
VAAPI = 'vaapi',
|
||||||
|
RKMPP = 'rkmpp',
|
||||||
|
DISABLED = 'disabled',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ToneMapping {
|
||||||
|
HABLE = 'hable',
|
||||||
|
MOBIUS = 'mobius',
|
||||||
|
REINHARD = 'reinhard',
|
||||||
|
DISABLED = 'disabled',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CQMode {
|
||||||
|
AUTO = 'auto',
|
||||||
|
CQP = 'cqp',
|
||||||
|
ICQ = 'icq',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Colorspace {
|
||||||
|
SRGB = 'srgb',
|
||||||
|
P3 = 'p3',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ImageFormat {
|
||||||
|
JPEG = 'jpeg',
|
||||||
|
WEBP = 'webp',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum LogLevel {
|
||||||
|
VERBOSE = 'verbose',
|
||||||
|
DEBUG = 'debug',
|
||||||
|
LOG = 'log',
|
||||||
|
WARN = 'warn',
|
||||||
|
ERROR = 'error',
|
||||||
|
FATAL = 'fatal',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SystemConfig {
|
||||||
|
ffmpeg: {
|
||||||
|
crf: number;
|
||||||
|
threads: number;
|
||||||
|
preset: string;
|
||||||
|
targetVideoCodec: VideoCodec;
|
||||||
|
acceptedVideoCodecs: VideoCodec[];
|
||||||
|
targetAudioCodec: AudioCodec;
|
||||||
|
acceptedAudioCodecs: AudioCodec[];
|
||||||
|
targetResolution: string;
|
||||||
|
maxBitrate: string;
|
||||||
|
bframes: number;
|
||||||
|
refs: number;
|
||||||
|
gopSize: number;
|
||||||
|
npl: number;
|
||||||
|
temporalAQ: boolean;
|
||||||
|
cqMode: CQMode;
|
||||||
|
twoPass: boolean;
|
||||||
|
preferredHwDevice: string;
|
||||||
|
transcode: TranscodePolicy;
|
||||||
|
accel: TranscodeHWAccel;
|
||||||
|
tonemap: ToneMapping;
|
||||||
|
};
|
||||||
|
job: Record<ConcurrentQueueName, { concurrency: number }>;
|
||||||
|
logging: {
|
||||||
|
enabled: boolean;
|
||||||
|
level: LogLevel;
|
||||||
|
};
|
||||||
|
machineLearning: {
|
||||||
|
enabled: boolean;
|
||||||
|
url: string;
|
||||||
|
clip: {
|
||||||
|
enabled: boolean;
|
||||||
|
modelName: string;
|
||||||
|
};
|
||||||
|
facialRecognition: {
|
||||||
|
enabled: boolean;
|
||||||
|
modelName: string;
|
||||||
|
minScore: number;
|
||||||
|
minFaces: number;
|
||||||
|
maxDistance: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
map: {
|
||||||
|
enabled: boolean;
|
||||||
|
lightStyle: string;
|
||||||
|
darkStyle: string;
|
||||||
|
};
|
||||||
|
reverseGeocoding: {
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
oauth: {
|
||||||
|
autoLaunch: boolean;
|
||||||
|
autoRegister: boolean;
|
||||||
|
buttonText: string;
|
||||||
|
clientId: string;
|
||||||
|
clientSecret: string;
|
||||||
|
defaultStorageQuota: number;
|
||||||
|
enabled: boolean;
|
||||||
|
issuerUrl: string;
|
||||||
|
mobileOverrideEnabled: boolean;
|
||||||
|
mobileRedirectUri: string;
|
||||||
|
scope: string;
|
||||||
|
signingAlgorithm: string;
|
||||||
|
storageLabelClaim: string;
|
||||||
|
storageQuotaClaim: string;
|
||||||
|
};
|
||||||
|
passwordLogin: {
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
storageTemplate: {
|
||||||
|
enabled: boolean;
|
||||||
|
hashVerificationEnabled: boolean;
|
||||||
|
template: string;
|
||||||
|
};
|
||||||
|
image: {
|
||||||
|
thumbnailFormat: ImageFormat;
|
||||||
|
thumbnailSize: number;
|
||||||
|
previewFormat: ImageFormat;
|
||||||
|
previewSize: number;
|
||||||
|
quality: number;
|
||||||
|
colorspace: Colorspace;
|
||||||
|
extractEmbedded: boolean;
|
||||||
|
};
|
||||||
|
newVersionCheck: {
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
trash: {
|
||||||
|
enabled: boolean;
|
||||||
|
days: number;
|
||||||
|
};
|
||||||
|
theme: {
|
||||||
|
customCss: string;
|
||||||
|
};
|
||||||
|
library: {
|
||||||
|
scan: {
|
||||||
|
enabled: boolean;
|
||||||
|
cronExpression: string;
|
||||||
|
};
|
||||||
|
watch: {
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
notifications: {
|
||||||
|
smtp: {
|
||||||
|
enabled: boolean;
|
||||||
|
from: string;
|
||||||
|
replyTo: string;
|
||||||
|
transport: {
|
||||||
|
ignoreCert: boolean;
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
server: {
|
||||||
|
externalDomain: string;
|
||||||
|
loginPageMessage: string;
|
||||||
|
};
|
||||||
|
user: {
|
||||||
|
deleteDelay: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaults = Object.freeze<SystemConfig>({
|
||||||
|
ffmpeg: {
|
||||||
|
crf: 23,
|
||||||
|
threads: 0,
|
||||||
|
preset: 'ultrafast',
|
||||||
|
targetVideoCodec: VideoCodec.H264,
|
||||||
|
acceptedVideoCodecs: [VideoCodec.H264],
|
||||||
|
targetAudioCodec: AudioCodec.AAC,
|
||||||
|
acceptedAudioCodecs: [AudioCodec.AAC, AudioCodec.MP3, AudioCodec.LIBOPUS],
|
||||||
|
targetResolution: '720',
|
||||||
|
maxBitrate: '0',
|
||||||
|
bframes: -1,
|
||||||
|
refs: 0,
|
||||||
|
gopSize: 0,
|
||||||
|
npl: 0,
|
||||||
|
temporalAQ: false,
|
||||||
|
cqMode: CQMode.AUTO,
|
||||||
|
twoPass: false,
|
||||||
|
preferredHwDevice: 'auto',
|
||||||
|
transcode: TranscodePolicy.REQUIRED,
|
||||||
|
tonemap: ToneMapping.HABLE,
|
||||||
|
accel: TranscodeHWAccel.DISABLED,
|
||||||
|
},
|
||||||
|
job: {
|
||||||
|
[QueueName.BACKGROUND_TASK]: { concurrency: 5 },
|
||||||
|
[QueueName.SMART_SEARCH]: { concurrency: 2 },
|
||||||
|
[QueueName.METADATA_EXTRACTION]: { concurrency: 5 },
|
||||||
|
[QueueName.FACE_DETECTION]: { concurrency: 2 },
|
||||||
|
[QueueName.SEARCH]: { concurrency: 5 },
|
||||||
|
[QueueName.SIDECAR]: { concurrency: 5 },
|
||||||
|
[QueueName.LIBRARY]: { concurrency: 5 },
|
||||||
|
[QueueName.MIGRATION]: { concurrency: 5 },
|
||||||
|
[QueueName.THUMBNAIL_GENERATION]: { concurrency: 5 },
|
||||||
|
[QueueName.VIDEO_CONVERSION]: { concurrency: 1 },
|
||||||
|
[QueueName.NOTIFICATION]: { concurrency: 5 },
|
||||||
|
},
|
||||||
|
logging: {
|
||||||
|
enabled: true,
|
||||||
|
level: LogLevel.LOG,
|
||||||
|
},
|
||||||
|
machineLearning: {
|
||||||
|
enabled: process.env.IMMICH_MACHINE_LEARNING_ENABLED !== 'false',
|
||||||
|
url: process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003',
|
||||||
|
clip: {
|
||||||
|
enabled: true,
|
||||||
|
modelName: 'ViT-B-32__openai',
|
||||||
|
},
|
||||||
|
facialRecognition: {
|
||||||
|
enabled: true,
|
||||||
|
modelName: 'buffalo_l',
|
||||||
|
minScore: 0.7,
|
||||||
|
maxDistance: 0.5,
|
||||||
|
minFaces: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map: {
|
||||||
|
enabled: true,
|
||||||
|
lightStyle: '',
|
||||||
|
darkStyle: '',
|
||||||
|
},
|
||||||
|
reverseGeocoding: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
oauth: {
|
||||||
|
autoLaunch: false,
|
||||||
|
autoRegister: true,
|
||||||
|
buttonText: 'Login with OAuth',
|
||||||
|
clientId: '',
|
||||||
|
clientSecret: '',
|
||||||
|
defaultStorageQuota: 0,
|
||||||
|
enabled: false,
|
||||||
|
issuerUrl: '',
|
||||||
|
mobileOverrideEnabled: false,
|
||||||
|
mobileRedirectUri: '',
|
||||||
|
scope: 'openid email profile',
|
||||||
|
signingAlgorithm: 'RS256',
|
||||||
|
storageLabelClaim: 'preferred_username',
|
||||||
|
storageQuotaClaim: 'immich_quota',
|
||||||
|
},
|
||||||
|
passwordLogin: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
storageTemplate: {
|
||||||
|
enabled: false,
|
||||||
|
hashVerificationEnabled: true,
|
||||||
|
template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
thumbnailFormat: ImageFormat.WEBP,
|
||||||
|
thumbnailSize: 250,
|
||||||
|
previewFormat: ImageFormat.JPEG,
|
||||||
|
previewSize: 1440,
|
||||||
|
quality: 80,
|
||||||
|
colorspace: Colorspace.P3,
|
||||||
|
extractEmbedded: false,
|
||||||
|
},
|
||||||
|
newVersionCheck: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
trash: {
|
||||||
|
enabled: true,
|
||||||
|
days: 30,
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
customCss: '',
|
||||||
|
},
|
||||||
|
library: {
|
||||||
|
scan: {
|
||||||
|
enabled: true,
|
||||||
|
cronExpression: CronExpression.EVERY_DAY_AT_MIDNIGHT,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
externalDomain: '',
|
||||||
|
loginPageMessage: '',
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
smtp: {
|
||||||
|
enabled: false,
|
||||||
|
from: '',
|
||||||
|
replyTo: '',
|
||||||
|
transport: {
|
||||||
|
ignoreCert: false,
|
||||||
|
host: '',
|
||||||
|
port: 587,
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
deleteDelay: 7,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const WHEN_DB_URL_SET = Joi.when('DB_URL', {
|
const WHEN_DB_URL_SET = Joi.when('DB_URL', {
|
||||||
is: Joi.exist(),
|
is: Joi.exist(),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { randomUUID } from 'node:crypto';
|
import { randomUUID } from 'node:crypto';
|
||||||
import { dirname, join, resolve } from 'node:path';
|
import { dirname, join, resolve } from 'node:path';
|
||||||
|
import { ImageFormat } from 'src/config';
|
||||||
import { APP_MEDIA_LOCATION } from 'src/constants';
|
import { APP_MEDIA_LOCATION } from 'src/constants';
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { AssetPathType, PathType, PersonPathType } from 'src/entities/move.entity';
|
import { AssetPathType, PathType, PersonPathType } from 'src/entities/move.entity';
|
||||||
import { PersonEntity } from 'src/entities/person.entity';
|
import { PersonEntity } from 'src/entities/person.entity';
|
||||||
import { ImageFormat } from 'src/entities/system-config.entity';
|
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
|
|
|
@ -1,172 +1,19 @@
|
||||||
import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/common';
|
import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/common';
|
||||||
import { CronExpression } from '@nestjs/schedule';
|
|
||||||
import AsyncLock from 'async-lock';
|
import AsyncLock from 'async-lock';
|
||||||
import { plainToInstance } from 'class-transformer';
|
import { plainToInstance } from 'class-transformer';
|
||||||
import { validate } from 'class-validator';
|
import { validate } from 'class-validator';
|
||||||
import { load as loadYaml } from 'js-yaml';
|
import { load as loadYaml } from 'js-yaml';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
import { SystemConfig, defaults } from 'src/config';
|
||||||
import { SystemConfigDto } from 'src/dtos/system-config.dto';
|
import { SystemConfigDto } from 'src/dtos/system-config.dto';
|
||||||
import {
|
import { SystemConfigEntity, SystemConfigKey, SystemConfigValue } from 'src/entities/system-config.entity';
|
||||||
AudioCodec,
|
|
||||||
CQMode,
|
|
||||||
Colorspace,
|
|
||||||
ImageFormat,
|
|
||||||
LogLevel,
|
|
||||||
SystemConfig,
|
|
||||||
SystemConfigEntity,
|
|
||||||
SystemConfigKey,
|
|
||||||
SystemConfigValue,
|
|
||||||
ToneMapping,
|
|
||||||
TranscodeHWAccel,
|
|
||||||
TranscodePolicy,
|
|
||||||
VideoCodec,
|
|
||||||
} from 'src/entities/system-config.entity';
|
|
||||||
import { DatabaseLock } from 'src/interfaces/database.interface';
|
import { DatabaseLock } from 'src/interfaces/database.interface';
|
||||||
import { QueueName } from 'src/interfaces/job.interface';
|
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { ISystemConfigRepository } from 'src/interfaces/system-config.interface';
|
import { ISystemConfigRepository } from 'src/interfaces/system-config.interface';
|
||||||
|
|
||||||
export type SystemConfigValidator = (config: SystemConfig, newConfig: SystemConfig) => void | Promise<void>;
|
export type SystemConfigValidator = (config: SystemConfig, newConfig: SystemConfig) => void | Promise<void>;
|
||||||
|
|
||||||
export const defaults = Object.freeze<SystemConfig>({
|
|
||||||
ffmpeg: {
|
|
||||||
crf: 23,
|
|
||||||
threads: 0,
|
|
||||||
preset: 'ultrafast',
|
|
||||||
targetVideoCodec: VideoCodec.H264,
|
|
||||||
acceptedVideoCodecs: [VideoCodec.H264],
|
|
||||||
targetAudioCodec: AudioCodec.AAC,
|
|
||||||
acceptedAudioCodecs: [AudioCodec.AAC, AudioCodec.MP3, AudioCodec.LIBOPUS],
|
|
||||||
targetResolution: '720',
|
|
||||||
maxBitrate: '0',
|
|
||||||
bframes: -1,
|
|
||||||
refs: 0,
|
|
||||||
gopSize: 0,
|
|
||||||
npl: 0,
|
|
||||||
temporalAQ: false,
|
|
||||||
cqMode: CQMode.AUTO,
|
|
||||||
twoPass: false,
|
|
||||||
preferredHwDevice: 'auto',
|
|
||||||
transcode: TranscodePolicy.REQUIRED,
|
|
||||||
tonemap: ToneMapping.HABLE,
|
|
||||||
accel: TranscodeHWAccel.DISABLED,
|
|
||||||
},
|
|
||||||
job: {
|
|
||||||
[QueueName.BACKGROUND_TASK]: { concurrency: 5 },
|
|
||||||
[QueueName.SMART_SEARCH]: { concurrency: 2 },
|
|
||||||
[QueueName.METADATA_EXTRACTION]: { concurrency: 5 },
|
|
||||||
[QueueName.FACE_DETECTION]: { concurrency: 2 },
|
|
||||||
[QueueName.SEARCH]: { concurrency: 5 },
|
|
||||||
[QueueName.SIDECAR]: { concurrency: 5 },
|
|
||||||
[QueueName.LIBRARY]: { concurrency: 5 },
|
|
||||||
[QueueName.MIGRATION]: { concurrency: 5 },
|
|
||||||
[QueueName.THUMBNAIL_GENERATION]: { concurrency: 5 },
|
|
||||||
[QueueName.VIDEO_CONVERSION]: { concurrency: 1 },
|
|
||||||
[QueueName.NOTIFICATION]: { concurrency: 5 },
|
|
||||||
},
|
|
||||||
logging: {
|
|
||||||
enabled: true,
|
|
||||||
level: LogLevel.LOG,
|
|
||||||
},
|
|
||||||
machineLearning: {
|
|
||||||
enabled: process.env.IMMICH_MACHINE_LEARNING_ENABLED !== 'false',
|
|
||||||
url: process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003',
|
|
||||||
clip: {
|
|
||||||
enabled: true,
|
|
||||||
modelName: 'ViT-B-32__openai',
|
|
||||||
},
|
|
||||||
facialRecognition: {
|
|
||||||
enabled: true,
|
|
||||||
modelName: 'buffalo_l',
|
|
||||||
minScore: 0.7,
|
|
||||||
maxDistance: 0.5,
|
|
||||||
minFaces: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
map: {
|
|
||||||
enabled: true,
|
|
||||||
lightStyle: '',
|
|
||||||
darkStyle: '',
|
|
||||||
},
|
|
||||||
reverseGeocoding: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
oauth: {
|
|
||||||
autoLaunch: false,
|
|
||||||
autoRegister: true,
|
|
||||||
buttonText: 'Login with OAuth',
|
|
||||||
clientId: '',
|
|
||||||
clientSecret: '',
|
|
||||||
defaultStorageQuota: 0,
|
|
||||||
enabled: false,
|
|
||||||
issuerUrl: '',
|
|
||||||
mobileOverrideEnabled: false,
|
|
||||||
mobileRedirectUri: '',
|
|
||||||
scope: 'openid email profile',
|
|
||||||
signingAlgorithm: 'RS256',
|
|
||||||
storageLabelClaim: 'preferred_username',
|
|
||||||
storageQuotaClaim: 'immich_quota',
|
|
||||||
},
|
|
||||||
passwordLogin: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
storageTemplate: {
|
|
||||||
enabled: false,
|
|
||||||
hashVerificationEnabled: true,
|
|
||||||
template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
|
|
||||||
},
|
|
||||||
image: {
|
|
||||||
thumbnailFormat: ImageFormat.WEBP,
|
|
||||||
thumbnailSize: 250,
|
|
||||||
previewFormat: ImageFormat.JPEG,
|
|
||||||
previewSize: 1440,
|
|
||||||
quality: 80,
|
|
||||||
colorspace: Colorspace.P3,
|
|
||||||
extractEmbedded: false,
|
|
||||||
},
|
|
||||||
newVersionCheck: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
trash: {
|
|
||||||
enabled: true,
|
|
||||||
days: 30,
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
customCss: '',
|
|
||||||
},
|
|
||||||
library: {
|
|
||||||
scan: {
|
|
||||||
enabled: true,
|
|
||||||
cronExpression: CronExpression.EVERY_DAY_AT_MIDNIGHT,
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
externalDomain: '',
|
|
||||||
loginPageMessage: '',
|
|
||||||
},
|
|
||||||
notifications: {
|
|
||||||
smtp: {
|
|
||||||
enabled: false,
|
|
||||||
from: '',
|
|
||||||
replyTo: '',
|
|
||||||
transport: {
|
|
||||||
ignoreCert: false,
|
|
||||||
host: '',
|
|
||||||
port: 587,
|
|
||||||
username: '',
|
|
||||||
password: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
deleteDelay: 7,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export enum FeatureFlag {
|
export enum FeatureFlag {
|
||||||
SMART_SEARCH = 'smartSearch',
|
SMART_SEARCH = 'smartSearch',
|
||||||
FACIAL_RECOGNITION = 'facialRecognition',
|
FACIAL_RECOGNITION = 'facialRecognition',
|
||||||
|
@ -192,7 +39,7 @@ export class SystemConfigCore {
|
||||||
private config: SystemConfig | null = null;
|
private config: SystemConfig | null = null;
|
||||||
private lastUpdated: number | null = null;
|
private lastUpdated: number | null = null;
|
||||||
|
|
||||||
public config$ = new Subject<SystemConfig>();
|
config$ = new Subject<SystemConfig>();
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
private repository: ISystemConfigRepository,
|
private repository: ISystemConfigRepository,
|
||||||
|
@ -267,11 +114,7 @@ export class SystemConfigCore {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDefaults(): SystemConfig {
|
async getConfig(force = false): Promise<SystemConfig> {
|
||||||
return defaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getConfig(force = false): Promise<SystemConfig> {
|
|
||||||
if (force || !this.config) {
|
if (force || !this.config) {
|
||||||
const lastUpdated = this.lastUpdated;
|
const lastUpdated = this.lastUpdated;
|
||||||
await this.asyncLock.acquire(DatabaseLock[DatabaseLock.GetSystemConfig], async () => {
|
await this.asyncLock.acquire(DatabaseLock[DatabaseLock.GetSystemConfig], async () => {
|
||||||
|
@ -285,7 +128,7 @@ export class SystemConfigCore {
|
||||||
return this.config!;
|
return this.config!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateConfig(newConfig: SystemConfig): Promise<SystemConfig> {
|
async updateConfig(newConfig: SystemConfig): Promise<SystemConfig> {
|
||||||
if (await this.hasFeature(FeatureFlag.CONFIG_FILE)) {
|
if (await this.hasFeature(FeatureFlag.CONFIG_FILE)) {
|
||||||
throw new BadRequestException('Cannot update configuration while IMMICH_CONFIG_FILE is in use');
|
throw new BadRequestException('Cannot update configuration while IMMICH_CONFIG_FILE is in use');
|
||||||
}
|
}
|
||||||
|
@ -328,9 +171,8 @@ export class SystemConfigCore {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async refreshConfig() {
|
async refreshConfig() {
|
||||||
const newConfig = await this.getConfig(true);
|
const newConfig = await this.getConfig(true);
|
||||||
|
|
||||||
this.config$.next(newConfig);
|
this.config$.next(newConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ import {
|
||||||
ValidatorConstraint,
|
ValidatorConstraint,
|
||||||
ValidatorConstraintInterface,
|
ValidatorConstraintInterface,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { CLIPConfig, RecognitionConfig } from 'src/dtos/model-config.dto';
|
|
||||||
import {
|
import {
|
||||||
AudioCodec,
|
AudioCodec,
|
||||||
CQMode,
|
CQMode,
|
||||||
|
@ -30,7 +29,8 @@ import {
|
||||||
TranscodeHWAccel,
|
TranscodeHWAccel,
|
||||||
TranscodePolicy,
|
TranscodePolicy,
|
||||||
VideoCodec,
|
VideoCodec,
|
||||||
} from 'src/entities/system-config.entity';
|
} from 'src/config';
|
||||||
|
import { CLIPConfig, RecognitionConfig } from 'src/dtos/model-config.dto';
|
||||||
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
|
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { ValidateBoolean, validateCronExpression } from 'src/validation';
|
import { ValidateBoolean, validateCronExpression } from 'src/validation';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ConcurrentQueueName } from 'src/interfaces/job.interface';
|
import { SystemConfig } from 'src/config';
|
||||||
import { Column, Entity, PrimaryColumn } from 'typeorm';
|
import { Column, Entity, PrimaryColumn } from 'typeorm';
|
||||||
|
|
||||||
export type SystemConfigValue = string | string[] | number | boolean;
|
export type SystemConfigValue = string | string[] | number | boolean;
|
||||||
|
@ -143,197 +143,3 @@ export class SystemConfigEntity<T = SystemConfigValue> {
|
||||||
@Column({ type: 'varchar', nullable: true, transformer: { to: JSON.stringify, from: JSON.parse } })
|
@Column({ type: 'varchar', nullable: true, transformer: { to: JSON.stringify, from: JSON.parse } })
|
||||||
value!: T;
|
value!: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TranscodePolicy {
|
|
||||||
ALL = 'all',
|
|
||||||
OPTIMAL = 'optimal',
|
|
||||||
BITRATE = 'bitrate',
|
|
||||||
REQUIRED = 'required',
|
|
||||||
DISABLED = 'disabled',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum TranscodeTarget {
|
|
||||||
NONE,
|
|
||||||
AUDIO,
|
|
||||||
VIDEO,
|
|
||||||
ALL,
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum VideoCodec {
|
|
||||||
H264 = 'h264',
|
|
||||||
HEVC = 'hevc',
|
|
||||||
VP9 = 'vp9',
|
|
||||||
AV1 = 'av1',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum AudioCodec {
|
|
||||||
MP3 = 'mp3',
|
|
||||||
AAC = 'aac',
|
|
||||||
LIBOPUS = 'libopus',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum TranscodeHWAccel {
|
|
||||||
NVENC = 'nvenc',
|
|
||||||
QSV = 'qsv',
|
|
||||||
VAAPI = 'vaapi',
|
|
||||||
RKMPP = 'rkmpp',
|
|
||||||
DISABLED = 'disabled',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ToneMapping {
|
|
||||||
HABLE = 'hable',
|
|
||||||
MOBIUS = 'mobius',
|
|
||||||
REINHARD = 'reinhard',
|
|
||||||
DISABLED = 'disabled',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CQMode {
|
|
||||||
AUTO = 'auto',
|
|
||||||
CQP = 'cqp',
|
|
||||||
ICQ = 'icq',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Colorspace {
|
|
||||||
SRGB = 'srgb',
|
|
||||||
P3 = 'p3',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ImageFormat {
|
|
||||||
JPEG = 'jpeg',
|
|
||||||
WEBP = 'webp',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum LogLevel {
|
|
||||||
VERBOSE = 'verbose',
|
|
||||||
DEBUG = 'debug',
|
|
||||||
LOG = 'log',
|
|
||||||
WARN = 'warn',
|
|
||||||
ERROR = 'error',
|
|
||||||
FATAL = 'fatal',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SystemConfig {
|
|
||||||
ffmpeg: {
|
|
||||||
crf: number;
|
|
||||||
threads: number;
|
|
||||||
preset: string;
|
|
||||||
targetVideoCodec: VideoCodec;
|
|
||||||
acceptedVideoCodecs: VideoCodec[];
|
|
||||||
targetAudioCodec: AudioCodec;
|
|
||||||
acceptedAudioCodecs: AudioCodec[];
|
|
||||||
targetResolution: string;
|
|
||||||
maxBitrate: string;
|
|
||||||
bframes: number;
|
|
||||||
refs: number;
|
|
||||||
gopSize: number;
|
|
||||||
npl: number;
|
|
||||||
temporalAQ: boolean;
|
|
||||||
cqMode: CQMode;
|
|
||||||
twoPass: boolean;
|
|
||||||
preferredHwDevice: string;
|
|
||||||
transcode: TranscodePolicy;
|
|
||||||
accel: TranscodeHWAccel;
|
|
||||||
tonemap: ToneMapping;
|
|
||||||
};
|
|
||||||
job: Record<ConcurrentQueueName, { concurrency: number }>;
|
|
||||||
logging: {
|
|
||||||
enabled: boolean;
|
|
||||||
level: LogLevel;
|
|
||||||
};
|
|
||||||
machineLearning: {
|
|
||||||
enabled: boolean;
|
|
||||||
url: string;
|
|
||||||
clip: {
|
|
||||||
enabled: boolean;
|
|
||||||
modelName: string;
|
|
||||||
};
|
|
||||||
facialRecognition: {
|
|
||||||
enabled: boolean;
|
|
||||||
modelName: string;
|
|
||||||
minScore: number;
|
|
||||||
minFaces: number;
|
|
||||||
maxDistance: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
map: {
|
|
||||||
enabled: boolean;
|
|
||||||
lightStyle: string;
|
|
||||||
darkStyle: string;
|
|
||||||
};
|
|
||||||
reverseGeocoding: {
|
|
||||||
enabled: boolean;
|
|
||||||
};
|
|
||||||
oauth: {
|
|
||||||
autoLaunch: boolean;
|
|
||||||
autoRegister: boolean;
|
|
||||||
buttonText: string;
|
|
||||||
clientId: string;
|
|
||||||
clientSecret: string;
|
|
||||||
defaultStorageQuota: number;
|
|
||||||
enabled: boolean;
|
|
||||||
issuerUrl: string;
|
|
||||||
mobileOverrideEnabled: boolean;
|
|
||||||
mobileRedirectUri: string;
|
|
||||||
scope: string;
|
|
||||||
signingAlgorithm: string;
|
|
||||||
storageLabelClaim: string;
|
|
||||||
storageQuotaClaim: string;
|
|
||||||
};
|
|
||||||
passwordLogin: {
|
|
||||||
enabled: boolean;
|
|
||||||
};
|
|
||||||
storageTemplate: {
|
|
||||||
enabled: boolean;
|
|
||||||
hashVerificationEnabled: boolean;
|
|
||||||
template: string;
|
|
||||||
};
|
|
||||||
image: {
|
|
||||||
thumbnailFormat: ImageFormat;
|
|
||||||
thumbnailSize: number;
|
|
||||||
previewFormat: ImageFormat;
|
|
||||||
previewSize: number;
|
|
||||||
quality: number;
|
|
||||||
colorspace: Colorspace;
|
|
||||||
extractEmbedded: boolean;
|
|
||||||
};
|
|
||||||
newVersionCheck: {
|
|
||||||
enabled: boolean;
|
|
||||||
};
|
|
||||||
trash: {
|
|
||||||
enabled: boolean;
|
|
||||||
days: number;
|
|
||||||
};
|
|
||||||
theme: {
|
|
||||||
customCss: string;
|
|
||||||
};
|
|
||||||
library: {
|
|
||||||
scan: {
|
|
||||||
enabled: boolean;
|
|
||||||
cronExpression: string;
|
|
||||||
};
|
|
||||||
watch: {
|
|
||||||
enabled: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
notifications: {
|
|
||||||
smtp: {
|
|
||||||
enabled: boolean;
|
|
||||||
from: string;
|
|
||||||
replyTo: string;
|
|
||||||
transport: {
|
|
||||||
ignoreCert: boolean;
|
|
||||||
host: string;
|
|
||||||
port: number;
|
|
||||||
username: string;
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
server: {
|
|
||||||
externalDomain: string;
|
|
||||||
loginPageMessage: string;
|
|
||||||
};
|
|
||||||
user: {
|
|
||||||
deleteDelay: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { SystemConfig } from 'src/config';
|
||||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||||
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server-info.dto';
|
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server-info.dto';
|
||||||
import { SystemConfig } from 'src/entities/system-config.entity';
|
|
||||||
|
|
||||||
export const IEventRepository = 'IEventRepository';
|
export const IEventRepository = 'IEventRepository';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { LogLevel } from 'src/entities/system-config.entity';
|
import { LogLevel } from 'src/config';
|
||||||
|
|
||||||
export const ILoggerRepository = 'ILoggerRepository';
|
export const ILoggerRepository = 'ILoggerRepository';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Writable } from 'node:stream';
|
import { Writable } from 'node:stream';
|
||||||
import { ImageFormat, TranscodeTarget, VideoCodec } from 'src/entities/system-config.entity';
|
import { ImageFormat, TranscodeTarget, VideoCodec } from 'src/config';
|
||||||
|
|
||||||
export const IMediaRepository = 'IMediaRepository';
|
export const IMediaRepository = 'IMediaRepository';
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ import { existsSync } from 'node:fs';
|
||||||
import { Worker } from 'node:worker_threads';
|
import { Worker } from 'node:worker_threads';
|
||||||
import sirv from 'sirv';
|
import sirv from 'sirv';
|
||||||
import { ApiModule, ImmichAdminModule } from 'src/app.module';
|
import { ApiModule, ImmichAdminModule } from 'src/app.module';
|
||||||
|
import { LogLevel } from 'src/config';
|
||||||
import { WEB_ROOT, envName, excludePaths, isDev, serverVersion } from 'src/constants';
|
import { WEB_ROOT, envName, excludePaths, isDev, serverVersion } from 'src/constants';
|
||||||
import { LogLevel } from 'src/entities/system-config.entity';
|
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { WebSocketAdapter } from 'src/middleware/websocket.adapter';
|
import { WebSocketAdapter } from 'src/middleware/websocket.adapter';
|
||||||
import { ApiService } from 'src/services/api.service';
|
import { ApiService } from 'src/services/api.service';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ConsoleLogger, Injectable, Scope } from '@nestjs/common';
|
import { ConsoleLogger, Injectable, Scope } from '@nestjs/common';
|
||||||
import { isLogLevelEnabled } from '@nestjs/common/services/utils/is-log-level-enabled.util';
|
import { isLogLevelEnabled } from '@nestjs/common/services/utils/is-log-level-enabled.util';
|
||||||
import { ClsService } from 'nestjs-cls';
|
import { ClsService } from 'nestjs-cls';
|
||||||
import { LogLevel } from 'src/entities/system-config.entity';
|
import { LogLevel } from 'src/config';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { LogColor } from 'src/utils/logger-colors';
|
import { LogColor } from 'src/utils/logger-colors';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import fs from 'node:fs/promises';
|
||||||
import { Writable } from 'node:stream';
|
import { Writable } from 'node:stream';
|
||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { Colorspace } from 'src/entities/system-config.entity';
|
import { Colorspace } from 'src/config';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import {
|
import {
|
||||||
IMediaRepository,
|
IMediaRepository,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import cookieParser from 'cookie';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { IncomingHttpHeaders } from 'node:http';
|
import { IncomingHttpHeaders } from 'node:http';
|
||||||
import { ClientMetadata, Issuer, UserinfoResponse, custom, generators } from 'openid-client';
|
import { ClientMetadata, Issuer, UserinfoResponse, custom, generators } from 'openid-client';
|
||||||
|
import { SystemConfig } from 'src/config';
|
||||||
import { AuthType, LOGIN_URL, MOBILE_REDIRECT } from 'src/constants';
|
import { AuthType, LOGIN_URL, MOBILE_REDIRECT } from 'src/constants';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
import { AccessCore } from 'src/cores/access.core';
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
|
@ -28,7 +29,6 @@ import {
|
||||||
mapLoginResponse,
|
mapLoginResponse,
|
||||||
} from 'src/dtos/auth.dto';
|
} from 'src/dtos/auth.dto';
|
||||||
import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
|
import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
|
||||||
import { SystemConfig } from 'src/entities/system-config.entity';
|
|
||||||
import { UserEntity } from 'src/entities/user.entity';
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IKeyRepository } from 'src/interfaces/api-key.interface';
|
import { IKeyRepository } from 'src/interfaces/api-key.interface';
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
|
import { SystemConfig } from 'src/config';
|
||||||
import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core';
|
import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import { SystemConfig, SystemConfigKey, SystemConfigKeyPaths } from 'src/entities/system-config.entity';
|
import { SystemConfigKey, SystemConfigKeyPaths } from 'src/entities/system-config.entity';
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import { Stats } from 'node:fs';
|
import { Stats } from 'node:fs';
|
||||||
|
import { SystemConfig } from 'src/config';
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import { mapLibrary } from 'src/dtos/library.dto';
|
import { mapLibrary } from 'src/dtos/library.dto';
|
||||||
import { AssetType } from 'src/entities/asset.entity';
|
import { AssetType } from 'src/entities/asset.entity';
|
||||||
import { LibraryType } from 'src/entities/library.entity';
|
import { LibraryType } from 'src/entities/library.entity';
|
||||||
import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity';
|
import { SystemConfigKey } from 'src/entities/system-config.entity';
|
||||||
import { UserEntity } from 'src/entities/user.entity';
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { Stats } from 'node:fs';
|
import { Stats } from 'node:fs';
|
||||||
import { AssetType } from 'src/entities/asset.entity';
|
|
||||||
import { ExifEntity } from 'src/entities/exif.entity';
|
|
||||||
import {
|
import {
|
||||||
AudioCodec,
|
AudioCodec,
|
||||||
Colorspace,
|
Colorspace,
|
||||||
ImageFormat,
|
ImageFormat,
|
||||||
SystemConfigKey,
|
|
||||||
ToneMapping,
|
ToneMapping,
|
||||||
TranscodeHWAccel,
|
TranscodeHWAccel,
|
||||||
TranscodePolicy,
|
TranscodePolicy,
|
||||||
VideoCodec,
|
VideoCodec,
|
||||||
} from 'src/entities/system-config.entity';
|
} from 'src/config';
|
||||||
|
import { AssetType } from 'src/entities/asset.entity';
|
||||||
|
import { ExifEntity } from 'src/entities/exif.entity';
|
||||||
|
import { SystemConfigKey } from 'src/entities/system-config.entity';
|
||||||
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
|
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||||
import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
|
import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import { Inject, Injectable, UnsupportedMediaTypeException } from '@nestjs/common';
|
import { Inject, Injectable, UnsupportedMediaTypeException } from '@nestjs/common';
|
||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
import { GeneratedImageType, StorageCore, StorageFolder } from 'src/cores/storage.core';
|
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
|
||||||
import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto';
|
|
||||||
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
|
|
||||||
import { AssetPathType } from 'src/entities/move.entity';
|
|
||||||
import {
|
import {
|
||||||
AudioCodec,
|
AudioCodec,
|
||||||
Colorspace,
|
Colorspace,
|
||||||
|
@ -13,7 +8,12 @@ import {
|
||||||
TranscodePolicy,
|
TranscodePolicy,
|
||||||
TranscodeTarget,
|
TranscodeTarget,
|
||||||
VideoCodec,
|
VideoCodec,
|
||||||
} from 'src/entities/system-config.entity';
|
} from 'src/config';
|
||||||
|
import { GeneratedImageType, StorageCore, StorageFolder } from 'src/cores/storage.core';
|
||||||
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
|
import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto';
|
||||||
|
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
|
||||||
|
import { AssetPathType } from 'src/entities/move.entity';
|
||||||
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
|
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||||
|
import { Colorspace } from 'src/config';
|
||||||
import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { PersonResponseDto, mapFaces, mapPerson } from 'src/dtos/person.dto';
|
import { PersonResponseDto, mapFaces, mapPerson } from 'src/dtos/person.dto';
|
||||||
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
||||||
import { Colorspace, SystemConfigKey } from 'src/entities/system-config.entity';
|
import { SystemConfigKey } from 'src/entities/system-config.entity';
|
||||||
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
|
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||||
import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
|
import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||||
|
import { ImageFormat } from 'src/config';
|
||||||
import { FACE_THUMBNAIL_SIZE } from 'src/constants';
|
import { FACE_THUMBNAIL_SIZE } from 'src/constants';
|
||||||
import { AccessCore, Permission } from 'src/cores/access.core';
|
import { AccessCore, Permission } from 'src/cores/access.core';
|
||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
|
@ -23,7 +24,6 @@ import {
|
||||||
} from 'src/dtos/person.dto';
|
} from 'src/dtos/person.dto';
|
||||||
import { PersonPathType } from 'src/entities/move.entity';
|
import { PersonPathType } from 'src/entities/move.entity';
|
||||||
import { PersonEntity } from 'src/entities/person.entity';
|
import { PersonEntity } from 'src/entities/person.entity';
|
||||||
import { ImageFormat } from 'src/entities/system-config.entity';
|
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
|
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||||
|
|
|
@ -82,7 +82,7 @@ export class ServerInfoService {
|
||||||
return serverVersion;
|
return serverVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFeatures(): Promise<ServerFeaturesDto> {
|
async getFeatures(): Promise<ServerFeaturesDto> {
|
||||||
return this.configCore.getFeatures();
|
return this.configCore.getFeatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { Stats } from 'node:fs';
|
import { Stats } from 'node:fs';
|
||||||
import { SystemConfigCore, defaults } from 'src/cores/system-config.core';
|
import { SystemConfig, defaults } from 'src/config';
|
||||||
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { AssetPathType } from 'src/entities/move.entity';
|
import { AssetPathType } from 'src/entities/move.entity';
|
||||||
import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity';
|
import { SystemConfigKey } from 'src/entities/system-config.entity';
|
||||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||||
|
|
|
@ -3,6 +3,7 @@ import handlebar from 'handlebars';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import sanitize from 'sanitize-filename';
|
import sanitize from 'sanitize-filename';
|
||||||
|
import { SystemConfig } from 'src/config';
|
||||||
import {
|
import {
|
||||||
supportedDayTokens,
|
supportedDayTokens,
|
||||||
supportedHourTokens,
|
supportedHourTokens,
|
||||||
|
@ -17,7 +18,6 @@ import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import { OnServerEvent } from 'src/decorators';
|
import { OnServerEvent } from 'src/decorators';
|
||||||
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
|
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
|
||||||
import { AssetPathType } from 'src/entities/move.entity';
|
import { AssetPathType } from 'src/entities/move.entity';
|
||||||
import { SystemConfig } from 'src/entities/system-config.entity';
|
|
||||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import { defaults } from 'src/cores/system-config.core';
|
|
||||||
import {
|
import {
|
||||||
AudioCodec,
|
AudioCodec,
|
||||||
CQMode,
|
CQMode,
|
||||||
|
@ -7,13 +6,13 @@ import {
|
||||||
ImageFormat,
|
ImageFormat,
|
||||||
LogLevel,
|
LogLevel,
|
||||||
SystemConfig,
|
SystemConfig,
|
||||||
SystemConfigEntity,
|
|
||||||
SystemConfigKey,
|
|
||||||
ToneMapping,
|
ToneMapping,
|
||||||
TranscodeHWAccel,
|
TranscodeHWAccel,
|
||||||
TranscodePolicy,
|
TranscodePolicy,
|
||||||
VideoCodec,
|
VideoCodec,
|
||||||
} from 'src/entities/system-config.entity';
|
defaults,
|
||||||
|
} from 'src/config';
|
||||||
|
import { SystemConfigEntity, SystemConfigKey } from 'src/entities/system-config.entity';
|
||||||
import { IEventRepository, ServerEvent } from 'src/interfaces/event.interface';
|
import { IEventRepository, ServerEvent } from 'src/interfaces/event.interface';
|
||||||
import { QueueName } from 'src/interfaces/job.interface';
|
import { QueueName } from 'src/interfaces/job.interface';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { instanceToPlain } from 'class-transformer';
|
import { instanceToPlain } from 'class-transformer';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { LogLevel, SystemConfig, defaults } from 'src/config';
|
||||||
import {
|
import {
|
||||||
supportedDayTokens,
|
supportedDayTokens,
|
||||||
supportedHourTokens,
|
supportedHourTokens,
|
||||||
|
@ -14,7 +15,6 @@ import {
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import { OnServerEvent } from 'src/decorators';
|
import { OnServerEvent } from 'src/decorators';
|
||||||
import { SystemConfigDto, SystemConfigTemplateStorageOptionDto, mapConfig } from 'src/dtos/system-config.dto';
|
import { SystemConfigDto, SystemConfigTemplateStorageOptionDto, mapConfig } from 'src/dtos/system-config.dto';
|
||||||
import { LogLevel, SystemConfig } from 'src/entities/system-config.entity';
|
|
||||||
import {
|
import {
|
||||||
ClientEvent,
|
ClientEvent,
|
||||||
IEventRepository,
|
IEventRepository,
|
||||||
|
@ -56,8 +56,7 @@ export class SystemConfigService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaults(): SystemConfigDto {
|
getDefaults(): SystemConfigDto {
|
||||||
const config = this.core.getDefaults();
|
return mapConfig(defaults);
|
||||||
return mapConfig(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnServerEvent(ServerAsyncEvent.CONFIG_VALIDATE)
|
@OnServerEvent(ServerAsyncEvent.CONFIG_VALIDATE)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
import { CQMode, ToneMapping, TranscodeHWAccel, TranscodeTarget, VideoCodec } from 'src/config';
|
||||||
import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto';
|
import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto';
|
||||||
import { CQMode, ToneMapping, TranscodeHWAccel, TranscodeTarget, VideoCodec } from 'src/entities/system-config.entity';
|
|
||||||
import {
|
import {
|
||||||
AudioStreamInfo,
|
AudioStreamInfo,
|
||||||
BitrateDistribution,
|
BitrateDistribution,
|
||||||
|
|
Loading…
Reference in a new issue