improve settings
This commit is contained in:
parent
95b06ffc63
commit
5160e19a35
@ -359,7 +359,7 @@ export class SettingsMWs {
|
|||||||
original.Client.Other.enableOnScrollThumbnailPrioritising = settings.Client.enableOnScrollThumbnailPrioritising;
|
original.Client.Other.enableOnScrollThumbnailPrioritising = settings.Client.enableOnScrollThumbnailPrioritising;
|
||||||
original.Client.Other.defaultPhotoSortingMethod = settings.Client.defaultPhotoSortingMethod;
|
original.Client.Other.defaultPhotoSortingMethod = settings.Client.defaultPhotoSortingMethod;
|
||||||
original.Client.Other.NavBar.showItemCount = settings.Client.NavBar.showItemCount;
|
original.Client.Other.NavBar.showItemCount = settings.Client.NavBar.showItemCount;
|
||||||
original.Server.Threading.enable = settings.Server.enable;
|
original.Server.Threading.enabled = settings.Server.enabled;
|
||||||
original.Server.Threading.thumbnailThreads = settings.Server.thumbnailThreads;
|
original.Server.Threading.thumbnailThreads = settings.Server.thumbnailThreads;
|
||||||
original.save();
|
original.save();
|
||||||
await ConfigDiagnostics.runDiagnostics();
|
await ConfigDiagnostics.runDiagnostics();
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export class DiskManager {
|
|||||||
static threadPool: DiskManagerTH = null;
|
static threadPool: DiskManagerTH = null;
|
||||||
|
|
||||||
public static init() {
|
public static init() {
|
||||||
if (Config.Server.Threading.enable === true) {
|
if (Config.Server.Threading.enabled === true) {
|
||||||
DiskManager.threadPool = new DiskManagerTH(1);
|
DiskManager.threadPool = new DiskManagerTH(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@ export class DiskManager {
|
|||||||
|
|
||||||
let directory: DirectoryDTO;
|
let directory: DirectoryDTO;
|
||||||
|
|
||||||
if (Config.Server.Threading.enable === true) {
|
if (Config.Server.Threading.enabled === true) {
|
||||||
directory = await DiskManager.threadPool.execute(relativeDirectoryName, settings);
|
directory = await DiskManager.threadPool.execute(relativeDirectoryName, settings);
|
||||||
} else {
|
} else {
|
||||||
directory = await DiskMangerWorker.scanDirectory(relativeDirectoryName, settings);
|
directory = await DiskMangerWorker.scanDirectory(relativeDirectoryName, settings);
|
||||||
|
|||||||
@ -81,13 +81,13 @@ export class ConfigDiagnostics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async testThumbnailLib(processingLibrary: ServerConfig.ThumbnailProcessingLib) {
|
static async testThumbnailLib(processingLibrary: ServerConfig.PhotoProcessingLib) {
|
||||||
switch (processingLibrary) {
|
switch (processingLibrary) {
|
||||||
case ServerConfig.ThumbnailProcessingLib.sharp:
|
case ServerConfig.PhotoProcessingLib.sharp:
|
||||||
const sharp = require('sharp');
|
const sharp = require('sharp');
|
||||||
sharp();
|
sharp();
|
||||||
break;
|
break;
|
||||||
case ServerConfig.ThumbnailProcessingLib.gm:
|
case ServerConfig.PhotoProcessingLib.gm:
|
||||||
const gm = require('gm');
|
const gm = require('gm');
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
gm(ProjectPath.FrontendFolder + '/assets/icon.png').size((err: Error) => {
|
gm(ProjectPath.FrontendFolder + '/assets/icon.png').size((err: Error) => {
|
||||||
@ -120,8 +120,10 @@ export class ConfigDiagnostics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static async testServerThumbnailConfig(thumbnailConfig: ServerConfig.ThumbnailConfig) {
|
public static async testServerThumbnailConfig(server: ServerConfig.ThumbnailConfig) {
|
||||||
await ConfigDiagnostics.testThumbnailLib(thumbnailConfig.processingLibrary);
|
if (server.personFaceMargin < 0 || server.personFaceMargin > 1) {
|
||||||
|
throw new Error('personFaceMargin should be between 0 and 1');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async testClientThumbnailConfig(thumbnailConfig: ClientConfig.ThumbnailConfig) {
|
static async testClientThumbnailConfig(thumbnailConfig: ClientConfig.ThumbnailConfig) {
|
||||||
@ -218,19 +220,19 @@ export class ConfigDiagnostics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.Server.Media.Thumbnail.processingLibrary !== ServerConfig.ThumbnailProcessingLib.Jimp) {
|
if (Config.Server.Media.photoProcessingLibrary !== ServerConfig.PhotoProcessingLib.Jimp) {
|
||||||
try {
|
try {
|
||||||
await ConfigDiagnostics.testThumbnailLib(Config.Server.Media.Thumbnail.processingLibrary);
|
await ConfigDiagnostics.testThumbnailLib(Config.Server.Media.photoProcessingLibrary);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
const err: Error = ex;
|
const err: Error = ex;
|
||||||
NotificationManager.warning('Thumbnail hardware acceleration is not possible.' +
|
NotificationManager.warning('Thumbnail hardware acceleration is not possible.' +
|
||||||
' \'' + ServerConfig.ThumbnailProcessingLib[Config.Server.Media.Thumbnail.processingLibrary] + '\' node module is not found.' +
|
' \'' + ServerConfig.PhotoProcessingLib[Config.Server.Media.photoProcessingLibrary] + '\' node module is not found.' +
|
||||||
' Falling back temporally to JS based thumbnail generation', err.toString());
|
' Falling back temporally to JS based thumbnail generation', err.toString());
|
||||||
Logger.warn(LOG_TAG, '[Thumbnail hardware acceleration] module error: ', err.toString());
|
Logger.warn(LOG_TAG, '[Thumbnail hardware acceleration] module error: ', err.toString());
|
||||||
Logger.warn(LOG_TAG, 'Thumbnail hardware acceleration is not possible.' +
|
Logger.warn(LOG_TAG, 'Thumbnail hardware acceleration is not possible.' +
|
||||||
' \'' + ServerConfig.ThumbnailProcessingLib[Config.Server.Media.Thumbnail.processingLibrary] + '\' node module is not found.' +
|
' \'' + ServerConfig.PhotoProcessingLib[Config.Server.Media.photoProcessingLibrary] + '\' node module is not found.' +
|
||||||
' Falling back temporally to JS based thumbnail generation');
|
' Falling back temporally to JS based thumbnail generation');
|
||||||
Config.Server.Media.Thumbnail.processingLibrary = ServerConfig.ThumbnailProcessingLib.Jimp;
|
Config.Server.Media.photoProcessingLibrary = ServerConfig.PhotoProcessingLib.Jimp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,4 +345,5 @@ export class ConfigDiagnostics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export class PhotoProcessing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Config.Server.Threading.enable === true) {
|
if (Config.Server.Threading.enabled === true) {
|
||||||
if (Config.Server.Threading.thumbnailThreads > 0) {
|
if (Config.Server.Threading.thumbnailThreads > 0) {
|
||||||
Config.Client.Media.Thumbnail.concurrentThumbnailGenerations = Config.Server.Threading.thumbnailThreads;
|
Config.Client.Media.Thumbnail.concurrentThumbnailGenerations = Config.Server.Threading.thumbnailThreads;
|
||||||
} else {
|
} else {
|
||||||
@ -32,12 +32,12 @@ export class PhotoProcessing {
|
|||||||
Config.Client.Media.Thumbnail.concurrentThumbnailGenerations = 1;
|
Config.Client.Media.Thumbnail.concurrentThumbnailGenerations = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.Server.Threading.enable === true &&
|
if (Config.Server.Threading.enabled === true &&
|
||||||
Config.Server.Media.Thumbnail.processingLibrary === ServerConfig.ThumbnailProcessingLib.Jimp) {
|
Config.Server.Media.photoProcessingLibrary === ServerConfig.PhotoProcessingLib.Jimp) {
|
||||||
this.taskQue = new ThumbnailTH(Config.Client.Media.Thumbnail.concurrentThumbnailGenerations);
|
this.taskQue = new ThumbnailTH(Config.Client.Media.Thumbnail.concurrentThumbnailGenerations);
|
||||||
} else {
|
} else {
|
||||||
this.taskQue = new TaskExecuter(Config.Client.Media.Thumbnail.concurrentThumbnailGenerations,
|
this.taskQue = new TaskExecuter(Config.Client.Media.Thumbnail.concurrentThumbnailGenerations,
|
||||||
(input => ThumbnailWorker.render(input, Config.Server.Media.Thumbnail.processingLibrary)));
|
(input => ThumbnailWorker.render(input, Config.Server.Media.photoProcessingLibrary)));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initDone = true;
|
this.initDone = true;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import {ITask} from './tasks/ITask';
|
|||||||
import {IndexingTask} from './tasks/IndexingTask';
|
import {IndexingTask} from './tasks/IndexingTask';
|
||||||
import {DBRestTask} from './tasks/DBResetTask';
|
import {DBRestTask} from './tasks/DBResetTask';
|
||||||
import {VideoConvertingTask} from './tasks/VideoConvertingTask';
|
import {VideoConvertingTask} from './tasks/VideoConvertingTask';
|
||||||
|
import {PhotoConvertingTask} from './tasks/PhotoConvertingTask';
|
||||||
|
|
||||||
export class TaskRepository {
|
export class TaskRepository {
|
||||||
|
|
||||||
@ -28,3 +29,4 @@ export class TaskRepository {
|
|||||||
TaskRepository.Instance.register(new IndexingTask());
|
TaskRepository.Instance.register(new IndexingTask());
|
||||||
TaskRepository.Instance.register(new DBRestTask());
|
TaskRepository.Instance.register(new DBRestTask());
|
||||||
TaskRepository.Instance.register(new VideoConvertingTask());
|
TaskRepository.Instance.register(new VideoConvertingTask());
|
||||||
|
TaskRepository.Instance.register(new PhotoConvertingTask());
|
||||||
|
|||||||
@ -2,17 +2,15 @@ import {TaskProgressDTO, TaskState} from '../../../../common/entities/settings/T
|
|||||||
import {ConfigTemplateEntry} from '../../../../common/entities/task/TaskDTO';
|
import {ConfigTemplateEntry} from '../../../../common/entities/task/TaskDTO';
|
||||||
import {Task} from './Task';
|
import {Task} from './Task';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as util from 'util';
|
|
||||||
import {DiskManager} from '../../DiskManger';
|
import {DiskManager} from '../../DiskManger';
|
||||||
import {DiskMangerWorker} from '../../threading/DiskMangerWorker';
|
import {DiskMangerWorker} from '../../threading/DiskMangerWorker';
|
||||||
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||||
|
import {Logger} from '../../../Logger';
|
||||||
|
|
||||||
declare var global: NodeJS.Global;
|
declare var global: NodeJS.Global;
|
||||||
|
|
||||||
|
|
||||||
const LOG_TAG = '[FileTask]';
|
const LOG_TAG = '[FileTask]';
|
||||||
const existsPr = util.promisify(fs.exists);
|
|
||||||
|
|
||||||
|
|
||||||
export abstract class FileTask<T> extends Task {
|
export abstract class FileTask<T> extends Task {
|
||||||
@ -58,7 +56,12 @@ export abstract class FileTask<T> extends Task {
|
|||||||
this.progress.left = this.fileQueue.length;
|
this.progress.left = this.fileQueue.length;
|
||||||
this.progress.progress++;
|
this.progress.progress++;
|
||||||
this.progress.comment = 'processing: ' + file;
|
this.progress.comment = 'processing: ' + file;
|
||||||
|
try {
|
||||||
await this.processFile(file);
|
await this.processFile(file);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
Logger.error(LOG_TAG, 'Error during processing file: ' + e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this.progress;
|
return this.progress;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,30 +1,18 @@
|
|||||||
import {TaskProgressDTO, TaskState} from '../../../../common/entities/settings/TaskProgressDTO';
|
import {TaskProgressDTO, TaskState} from '../../../../common/entities/settings/TaskProgressDTO';
|
||||||
import {ObjectManagers} from '../../ObjectManagers';
|
import {ObjectManagers} from '../../ObjectManagers';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
|
||||||
import {Logger} from '../../../Logger';
|
|
||||||
import {RendererInput, ThumbnailSourceType, ThumbnailWorker} from '../../threading/ThumbnailWorker';
|
|
||||||
import {Config} from '../../../../common/config/private/Config';
|
import {Config} from '../../../../common/config/private/Config';
|
||||||
import {MediaDTO} from '../../../../common/entities/MediaDTO';
|
|
||||||
import {ProjectPath} from '../../../ProjectPath';
|
|
||||||
import {ThumbnailGeneratorMWs} from '../../../middlewares/thumbnail/ThumbnailGeneratorMWs';
|
|
||||||
import {Task} from './Task';
|
import {Task} from './Task';
|
||||||
import {ConfigTemplateEntry, DefaultsTasks} from '../../../../common/entities/task/TaskDTO';
|
import {ConfigTemplateEntry, DefaultsTasks} from '../../../../common/entities/task/TaskDTO';
|
||||||
import {ServerConfig} from '../../../../common/config/private/IPrivateConfig';
|
import {ServerConfig} from '../../../../common/config/private/IPrivateConfig';
|
||||||
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
|
|
||||||
|
|
||||||
declare var global: NodeJS.Global;
|
declare var global: NodeJS.Global;
|
||||||
const LOG_TAG = '[IndexingTask]';
|
const LOG_TAG = '[IndexingTask]';
|
||||||
|
|
||||||
export class IndexingTask extends Task<{ createThumbnails: boolean }> {
|
export class IndexingTask extends Task {
|
||||||
public readonly Name = DefaultsTasks[DefaultsTasks.Indexing];
|
public readonly Name = DefaultsTasks[DefaultsTasks.Indexing];
|
||||||
directoriesToIndex: string[] = [];
|
directoriesToIndex: string[] = [];
|
||||||
public readonly ConfigTemplate: ConfigTemplateEntry[] = [{
|
public readonly ConfigTemplate: ConfigTemplateEntry[] = null;
|
||||||
id: 'createThumbnails',
|
|
||||||
type: 'boolean',
|
|
||||||
name: 'With thumbnails',
|
|
||||||
defaultValue: false
|
|
||||||
}];
|
|
||||||
|
|
||||||
public get Supported(): boolean {
|
public get Supported(): boolean {
|
||||||
return Config.Server.Database.type !== ServerConfig.DatabaseType.memory;
|
return Config.Server.Database.type !== ServerConfig.DatabaseType.memory;
|
||||||
@ -54,31 +42,6 @@ export class IndexingTask extends Task<{ createThumbnails: boolean }> {
|
|||||||
for (let i = 0; i < scanned.directories.length; i++) {
|
for (let i = 0; i < scanned.directories.length; i++) {
|
||||||
this.directoriesToIndex.push(path.join(scanned.directories[i].path, scanned.directories[i].name));
|
this.directoriesToIndex.push(path.join(scanned.directories[i].path, scanned.directories[i].name));
|
||||||
}
|
}
|
||||||
if (this.config.createThumbnails) {
|
|
||||||
for (let i = 0; i < scanned.media.length; i++) {
|
|
||||||
try {
|
|
||||||
const media = scanned.media[i];
|
|
||||||
const mPath = path.join(ProjectPath.ImageFolder, media.directory.path, media.directory.name, media.name);
|
|
||||||
const thPath = path.join(ProjectPath.ThumbnailFolder,
|
|
||||||
PhotoProcessing.generateThumbnailName(mPath, Config.Client.Media.Thumbnail.thumbnailSizes[0]));
|
|
||||||
if (fs.existsSync(thPath)) { // skip existing thumbnails
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
await ThumbnailWorker.render(<RendererInput>{
|
|
||||||
type: MediaDTO.isVideo(media) ? ThumbnailSourceType.Video : ThumbnailSourceType.Photo,
|
|
||||||
mediaPath: mPath,
|
|
||||||
size: Config.Client.Media.Thumbnail.thumbnailSizes[0],
|
|
||||||
outPath: thPath,
|
|
||||||
makeSquare: false,
|
|
||||||
qualityPriority: Config.Server.Media.Thumbnail.qualityPriority
|
|
||||||
}, Config.Server.Media.Thumbnail.processingLibrary);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
Logger.error(LOG_TAG, 'Error during indexing job: ' + e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return this.progress;
|
return this.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,14 +14,14 @@ const existsPr = util.promisify(fs.exists);
|
|||||||
|
|
||||||
|
|
||||||
export class PhotoConvertingTask extends FileTask<string> {
|
export class PhotoConvertingTask extends FileTask<string> {
|
||||||
public readonly Name = DefaultsTasks[DefaultsTasks['Video Converting']];
|
public readonly Name = DefaultsTasks[DefaultsTasks['Photo Converting']];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({noVideo: true, noMetaFile: true});
|
super({noVideo: true, noMetaFile: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get Supported(): boolean {
|
public get Supported(): boolean {
|
||||||
return Config.Server.Media.Photo.Converting.enabled === true;
|
return Config.Client.Media.Photo.Converting.enabled === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async processDirectory(directory: DirectoryDTO): Promise<string[]> {
|
protected async processDirectory(directory: DirectoryDTO): Promise<string[]> {
|
||||||
@ -40,7 +40,7 @@ export class PhotoConvertingTask extends FileTask<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async processFile(file: string): Promise<void> {
|
protected async processFile(file: string): Promise<void> {
|
||||||
await PhotoProcessing.generateThumbnail(file, Config.Server.Media.Photo.Converting.resolution, ThumbnailSourceType.Photo, false);
|
await PhotoProcessing.convertPhoto(file, Config.Server.Media.Photo.Converting.resolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -103,7 +103,7 @@ export class ThumbnailTH extends ThreadPool<void> implements ITaskExecuter<Rende
|
|||||||
return super.executeTask(<ThumbnailTask>{
|
return super.executeTask(<ThumbnailTask>{
|
||||||
type: WorkerTaskTypes.thumbnail,
|
type: WorkerTaskTypes.thumbnail,
|
||||||
input: input,
|
input: input,
|
||||||
renderer: Config.Server.Media.Thumbnail.processingLibrary
|
renderer: Config.Server.Media.photoProcessingLibrary
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,16 +9,16 @@ export class ThumbnailWorker {
|
|||||||
|
|
||||||
private static imageRenderer: (input: RendererInput) => Promise<void> = null;
|
private static imageRenderer: (input: RendererInput) => Promise<void> = null;
|
||||||
private static videoRenderer: (input: RendererInput) => Promise<void> = null;
|
private static videoRenderer: (input: RendererInput) => Promise<void> = null;
|
||||||
private static rendererType: ServerConfig.ThumbnailProcessingLib = null;
|
private static rendererType: ServerConfig.PhotoProcessingLib = null;
|
||||||
|
|
||||||
public static render(input: RendererInput, renderer: ServerConfig.ThumbnailProcessingLib): Promise<void> {
|
public static render(input: RendererInput, renderer: ServerConfig.PhotoProcessingLib): Promise<void> {
|
||||||
if (input.type === ThumbnailSourceType.Photo) {
|
if (input.type === ThumbnailSourceType.Photo) {
|
||||||
return this.renderFromImage(input, renderer);
|
return this.renderFromImage(input, renderer);
|
||||||
}
|
}
|
||||||
return this.renderFromVideo(input);
|
return this.renderFromVideo(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static renderFromImage(input: RendererInput, renderer: ServerConfig.ThumbnailProcessingLib): Promise<void> {
|
public static renderFromImage(input: RendererInput, renderer: ServerConfig.PhotoProcessingLib): Promise<void> {
|
||||||
if (ThumbnailWorker.rendererType !== renderer) {
|
if (ThumbnailWorker.rendererType !== renderer) {
|
||||||
ThumbnailWorker.imageRenderer = ImageRendererFactory.build(renderer);
|
ThumbnailWorker.imageRenderer = ImageRendererFactory.build(renderer);
|
||||||
ThumbnailWorker.rendererType = renderer;
|
ThumbnailWorker.rendererType = renderer;
|
||||||
@ -117,13 +117,13 @@ export class VideoRendererFactory {
|
|||||||
|
|
||||||
export class ImageRendererFactory {
|
export class ImageRendererFactory {
|
||||||
|
|
||||||
public static build(renderer: ServerConfig.ThumbnailProcessingLib): (input: RendererInput) => Promise<void> {
|
public static build(renderer: ServerConfig.PhotoProcessingLib): (input: RendererInput) => Promise<void> {
|
||||||
switch (renderer) {
|
switch (renderer) {
|
||||||
case ServerConfig.ThumbnailProcessingLib.Jimp:
|
case ServerConfig.PhotoProcessingLib.Jimp:
|
||||||
return ImageRendererFactory.Jimp();
|
return ImageRendererFactory.Jimp();
|
||||||
case ServerConfig.ThumbnailProcessingLib.gm:
|
case ServerConfig.PhotoProcessingLib.gm:
|
||||||
return ImageRendererFactory.Gm();
|
return ImageRendererFactory.Gm();
|
||||||
case ServerConfig.ThumbnailProcessingLib.sharp:
|
case ServerConfig.PhotoProcessingLib.sharp:
|
||||||
return ImageRendererFactory.Sharp();
|
return ImageRendererFactory.Sharp();
|
||||||
}
|
}
|
||||||
throw new Error('unknown renderer');
|
throw new Error('unknown renderer');
|
||||||
|
|||||||
@ -54,7 +54,7 @@ export interface DiskManagerTask extends WorkerTask {
|
|||||||
|
|
||||||
export interface ThumbnailTask extends WorkerTask {
|
export interface ThumbnailTask extends WorkerTask {
|
||||||
input: RendererInput;
|
input: RendererInput;
|
||||||
renderer: ServerConfig.ThumbnailProcessingLib;
|
renderer: ServerConfig.PhotoProcessingLib;
|
||||||
}
|
}
|
||||||
|
|
||||||
export module WorkerTask {
|
export module WorkerTask {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export module ServerConfig {
|
|||||||
none = 1, error = 2, all = 3
|
none = 1, error = 2, all = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ThumbnailProcessingLib {
|
export enum PhotoProcessingLib {
|
||||||
sharp = 3,
|
sharp = 3,
|
||||||
Jimp = 1,
|
Jimp = 1,
|
||||||
gm = 2,
|
gm = 2,
|
||||||
@ -43,7 +43,6 @@ export module ServerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ThumbnailConfig {
|
export interface ThumbnailConfig {
|
||||||
processingLibrary: ThumbnailProcessingLib;
|
|
||||||
qualityPriority: boolean;
|
qualityPriority: boolean;
|
||||||
personFaceMargin: number; // in ration [0-1]
|
personFaceMargin: number; // in ration [0-1]
|
||||||
}
|
}
|
||||||
@ -65,7 +64,7 @@ export module ServerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ThreadingConfig {
|
export interface ThreadingConfig {
|
||||||
enable: boolean;
|
enabled: boolean;
|
||||||
thumbnailThreads: number;
|
thumbnailThreads: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +106,7 @@ export module ServerConfig {
|
|||||||
export interface MediaConfig {
|
export interface MediaConfig {
|
||||||
folder: string;
|
folder: string;
|
||||||
tempFolder: string;
|
tempFolder: string;
|
||||||
|
photoProcessingLibrary: PhotoProcessingLib;
|
||||||
Video: VideoConfig;
|
Video: VideoConfig;
|
||||||
Photo: PhotoConfig;
|
Photo: PhotoConfig;
|
||||||
Thumbnail: ThumbnailConfig;
|
Thumbnail: ThumbnailConfig;
|
||||||
|
|||||||
@ -14,8 +14,8 @@ export class PrivateConfigDefaultsClass extends PublicConfigClass implements IPr
|
|||||||
Media: {
|
Media: {
|
||||||
folder: 'demo/images',
|
folder: 'demo/images',
|
||||||
tempFolder: 'demo/tmp',
|
tempFolder: 'demo/tmp',
|
||||||
|
photoProcessingLibrary: ServerConfig.PhotoProcessingLib.sharp,
|
||||||
Thumbnail: {
|
Thumbnail: {
|
||||||
processingLibrary: ServerConfig.ThumbnailProcessingLib.sharp,
|
|
||||||
qualityPriority: true,
|
qualityPriority: true,
|
||||||
personFaceMargin: 0.6
|
personFaceMargin: 0.6
|
||||||
},
|
},
|
||||||
@ -61,7 +61,7 @@ export class PrivateConfigDefaultsClass extends PublicConfigClass implements IPr
|
|||||||
updateTimeout: 1000 * 60 * 5
|
updateTimeout: 1000 * 60 * 5
|
||||||
},
|
},
|
||||||
Threading: {
|
Threading: {
|
||||||
enable: true,
|
enabled: true,
|
||||||
thumbnailThreads: 0
|
thumbnailThreads: 0
|
||||||
},
|
},
|
||||||
Indexing: {
|
Indexing: {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ export type fieldType = 'string' | 'number' | 'boolean';
|
|||||||
|
|
||||||
|
|
||||||
export enum DefaultsTasks {
|
export enum DefaultsTasks {
|
||||||
Indexing = 1, 'Database Reset' = 2, 'Video Converting' = 3
|
Indexing = 1, 'Database Reset' = 2, 'Video Converting' = 3, 'Photo Converting' = 4, 'Thumbnail Generation' = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigTemplateEntry {
|
export interface ConfigTemplateEntry {
|
||||||
|
|||||||
@ -87,6 +87,7 @@ import {TimepickerModule} from 'ngx-bootstrap/timepicker';
|
|||||||
import {TimeStampDatePickerComponent} from './ui/utils/timestamp-datepicker/datepicker.component';
|
import {TimeStampDatePickerComponent} from './ui/utils/timestamp-datepicker/datepicker.component';
|
||||||
import {TimeStampTimePickerComponent} from './ui/utils/timestamp-timepicker/timepicker.component';
|
import {TimeStampTimePickerComponent} from './ui/utils/timestamp-timepicker/timepicker.component';
|
||||||
import {TasksProgressComponent} from './ui/settings/tasks/progress/progress.tasks.settings.component';
|
import {TasksProgressComponent} from './ui/settings/tasks/progress/progress.tasks.settings.component';
|
||||||
|
import {PhotoSettingsComponent} from './ui/settings/photo/photo.settings.component';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -188,6 +189,7 @@ export function translationsFactory(locale: string) {
|
|||||||
MapSettingsComponent,
|
MapSettingsComponent,
|
||||||
ThumbnailSettingsComponent,
|
ThumbnailSettingsComponent,
|
||||||
VideoSettingsComponent,
|
VideoSettingsComponent,
|
||||||
|
PhotoSettingsComponent,
|
||||||
MetaFileSettingsComponent,
|
MetaFileSettingsComponent,
|
||||||
SearchSettingsComponent,
|
SearchSettingsComponent,
|
||||||
ShareSettingsComponent,
|
ShareSettingsComponent,
|
||||||
|
|||||||
@ -53,8 +53,10 @@
|
|||||||
<button class="btn btn-link nav-link text-md-left py-md-1 px-md-0"
|
<button class="btn btn-link nav-link text-md-left py-md-1 px-md-0"
|
||||||
*ngFor="let s of contents; let i=index;"
|
*ngFor="let s of contents; let i=index;"
|
||||||
(click)="scrollTo(i)"
|
(click)="scrollTo(i)"
|
||||||
[hidden]="!s.hasAvailableSettings"
|
[hidden]="!s.HasAvailableSettings">
|
||||||
>{{s.Name}}</button>
|
{{s.Name}}<!--
|
||||||
|
--><ng-container *ngIf="s.Changed">*</ng-container>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -63,44 +65,47 @@
|
|||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<app-settings-basic #setting #basic
|
<app-settings-basic #setting #basic
|
||||||
[simplifiedMode]="simplifiedMode"
|
[simplifiedMode]="simplifiedMode"
|
||||||
[hidden]="!basic.hasAvailableSettings"></app-settings-basic>
|
[hidden]="!basic.HasAvailableSettings"></app-settings-basic>
|
||||||
<app-settings-usermanager #setting #userManager
|
<app-settings-usermanager #setting #userManager
|
||||||
[hidden]="!userManager.hasAvailableSettings"></app-settings-usermanager>
|
[hidden]="!userManager.HasAvailableSettings"></app-settings-usermanager>
|
||||||
<app-settings-database #setting #database
|
<app-settings-database #setting #database
|
||||||
[simplifiedMode]="simplifiedMode"
|
[simplifiedMode]="simplifiedMode"
|
||||||
[hidden]="!database.hasAvailableSettings"></app-settings-database>
|
[hidden]="!database.HasAvailableSettings"></app-settings-database>
|
||||||
<app-settings-thumbnail #setting #thumbnail
|
<app-settings-photo #setting #photo
|
||||||
[hidden]="!thumbnail.hasAvailableSettings"
|
[hidden]="!photo.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
|
[simplifiedMode]="simplifiedMode"></app-settings-photo>
|
||||||
<app-settings-video #setting #video
|
<app-settings-video #setting #video
|
||||||
[hidden]="!video.hasAvailableSettings"
|
[hidden]="!video.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-video>
|
[simplifiedMode]="simplifiedMode"></app-settings-video>
|
||||||
|
<app-settings-thumbnail #setting #thumbnail
|
||||||
|
[hidden]="!thumbnail.HasAvailableSettings"
|
||||||
|
[simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
|
||||||
<app-settings-search #setting #search
|
<app-settings-search #setting #search
|
||||||
[hidden]="!search.hasAvailableSettings"
|
[hidden]="!search.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-search>
|
[simplifiedMode]="simplifiedMode"></app-settings-search>
|
||||||
<app-settings-share #setting #share
|
<app-settings-share #setting #share
|
||||||
[hidden]="!share.hasAvailableSettings"
|
[hidden]="!share.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-share>
|
[simplifiedMode]="simplifiedMode"></app-settings-share>
|
||||||
<app-settings-map #setting #map
|
<app-settings-map #setting #map
|
||||||
[hidden]="!map.hasAvailableSettings"
|
[hidden]="!map.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-map>
|
[simplifiedMode]="simplifiedMode"></app-settings-map>
|
||||||
<app-settings-meta-file #setting #metaFile
|
<app-settings-meta-file #setting #metaFile
|
||||||
[hidden]="!metaFile.hasAvailableSettings"
|
[hidden]="!metaFile.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-meta-file>
|
[simplifiedMode]="simplifiedMode"></app-settings-meta-file>
|
||||||
<app-settings-other #setting #other
|
<app-settings-other #setting #other
|
||||||
[hidden]="!other.hasAvailableSettings"
|
[hidden]="!other.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-other>
|
[simplifiedMode]="simplifiedMode"></app-settings-other>
|
||||||
<app-settings-random-photo #setting #random
|
<app-settings-random-photo #setting #random
|
||||||
[hidden]="!random.hasAvailableSettings"
|
[hidden]="!random.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-random-photo>
|
[simplifiedMode]="simplifiedMode"></app-settings-random-photo>
|
||||||
<app-settings-faces #setting #faces
|
<app-settings-faces #setting #faces
|
||||||
[hidden]="!faces.hasAvailableSettings"
|
[hidden]="!faces.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-faces>
|
[simplifiedMode]="simplifiedMode"></app-settings-faces>
|
||||||
<app-settings-indexing #setting #indexing
|
<app-settings-indexing #setting #indexing
|
||||||
[hidden]="!indexing.hasAvailableSettings"
|
[hidden]="!indexing.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-indexing>
|
[simplifiedMode]="simplifiedMode"></app-settings-indexing>
|
||||||
<app-settings-tasks #setting #tasks
|
<app-settings-tasks #setting #tasks
|
||||||
[hidden]="!tasks.hasAvailableSettings"
|
[hidden]="!tasks.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-tasks>
|
[simplifiedMode]="simplifiedMode"></app-settings-tasks>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
export interface ISettingsComponent {
|
export interface ISettingsComponent {
|
||||||
|
|
||||||
hasAvailableSettings: boolean;
|
HasAvailableSettings: boolean;
|
||||||
Name: string;
|
Name: string;
|
||||||
|
Changed: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,6 +60,14 @@ export abstract class SettingsComponent<T extends { [key: string]: any }, S exte
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get Changed(): boolean {
|
||||||
|
return this.changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
get HasAvailableSettings(): boolean {
|
||||||
|
return this.hasAvailableSettings;
|
||||||
|
}
|
||||||
|
|
||||||
onNewSettings = (s: IPrivateConfig) => {
|
onNewSettings = (s: IPrivateConfig) => {
|
||||||
this.settings = Utils.clone(this.sliceFN(s));
|
this.settings = Utils.clone(this.sliceFN(s));
|
||||||
this.original = Utils.clone(this.settings);
|
this.original = Utils.clone(this.settings);
|
||||||
|
|||||||
@ -18,7 +18,12 @@ import {UserRoles} from '../../../../../common/entities/UserDTO';
|
|||||||
})
|
})
|
||||||
export class FacesSettingsComponent extends SettingsComponent<ClientConfig.FacesConfig> {
|
export class FacesSettingsComponent extends SettingsComponent<ClientConfig.FacesConfig> {
|
||||||
|
|
||||||
public userRoles: Array<any> = [];
|
public readonly userRoles = Utils
|
||||||
|
.enumToArray(UserRoles)
|
||||||
|
.filter(r => r.key !== UserRoles.LimitedGuest)
|
||||||
|
.filter(r => r.key <= this._authService.user.value.role)
|
||||||
|
.sort((a, b) => a.key - b.key);
|
||||||
|
|
||||||
constructor(_authService: AuthenticationService,
|
constructor(_authService: AuthenticationService,
|
||||||
_navigation: NavigationService,
|
_navigation: NavigationService,
|
||||||
_settingsService: FacesSettingsService,
|
_settingsService: FacesSettingsService,
|
||||||
@ -26,11 +31,6 @@ export class FacesSettingsComponent extends SettingsComponent<ClientConfig.Faces
|
|||||||
i18n: I18n) {
|
i18n: I18n) {
|
||||||
super(i18n('Faces'), _authService, _navigation, _settingsService, notification, i18n, s => s.Client.Faces);
|
super(i18n('Faces'), _authService, _navigation, _settingsService, notification, i18n, s => s.Client.Faces);
|
||||||
|
|
||||||
this.userRoles = Utils
|
|
||||||
.enumToArray(UserRoles)
|
|
||||||
.filter(r => r.key !== UserRoles.LimitedGuest)
|
|
||||||
.filter(r => r.key <= this._authService.user.value.role)
|
|
||||||
.sort((a, b) => a.key - b.key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -99,43 +99,39 @@
|
|||||||
<hr/>
|
<hr/>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<div class="alert alert-secondary" role="alert">
|
||||||
<ng-container i18n>If you add a new folder to your gallery, the site indexes it automatically.</ng-container>
|
<ng-container i18n>If you add a new folder to your gallery, the site indexes it automatically.</ng-container>
|
||||||
<ng-container i18n>If you would like to trigger indexing manually, click index button.</ng-container>
|
<ng-container i18n>If you would like to trigger indexing manually, click index button.</ng-container>
|
||||||
<br/>
|
<br/>
|
||||||
(
|
(<ng-container i18n>Note: search only works among the indexed directories</ng-container>)
|
||||||
<ng-container i18n>Note: search only works among the indexed directories</ng-container>
|
</div>
|
||||||
)<br/>
|
|
||||||
|
|
||||||
|
|
||||||
<div *ngIf="Progress != null">
|
<div *ngIf="Progress != null">
|
||||||
<app-settings-tasks-progress [progress]="Progress"></app-settings-tasks-progress>
|
<app-settings-tasks-progress [progress]="Progress"></app-settings-tasks-progress>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row justify-content-center buttons-row">
|
|
||||||
<button class="btn btn-success"
|
<button class="btn btn-success ml-0"
|
||||||
*ngIf="Progress == null"
|
*ngIf="Progress == null"
|
||||||
[disabled]="inProgress"
|
[disabled]="inProgress"
|
||||||
title="Indexes the folders"
|
title="Indexes the folders"
|
||||||
i18n-title
|
i18n-title
|
||||||
(click)="index(false)" i18n>Index
|
(click)="index()">
|
||||||
|
<ng-container i18n>Index folders now</ng-container>
|
||||||
|
<span class="oi oi-media-play ml-2"></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary"
|
<button class="btn btn-secondary ml-0"
|
||||||
title="Indexes the folders and also creates the thumbnails"
|
|
||||||
i18n-title
|
|
||||||
*ngIf="Progress == null"
|
|
||||||
[disabled]="inProgress"
|
|
||||||
(click)="index(true)" i18n>Index with Thumbnails
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-secondary"
|
|
||||||
*ngIf="Progress != null"
|
*ngIf="Progress != null"
|
||||||
[disabled]="inProgress || Progress.state !== TaskState.running"
|
[disabled]="inProgress || Progress.state !== TaskState.running"
|
||||||
(click)="cancelIndexing()" i18n>Cancel
|
(click)="cancelIndexing()" i18n>Cancel converting
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger"
|
<button class="btn btn-danger ml-2"
|
||||||
[disabled]="inProgress"
|
[disabled]="inProgress"
|
||||||
(click)="resetDatabase()" i18n>Reset Indexes
|
(click)="resetDatabase()" i18n>Reset Indexes
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
<div class="row statics">
|
<div class="row statics">
|
||||||
<div class="col-md-4 col-12" i18n>
|
<div class="col-md-4 col-12" i18n>
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export class IndexingSettingsComponent extends SettingsComponent<ServerConfig.In
|
|||||||
super(i18n('Folder indexing'),
|
super(i18n('Folder indexing'),
|
||||||
_authService,
|
_authService,
|
||||||
_navigation,
|
_navigation,
|
||||||
<any>_settingsService,
|
_settingsService,
|
||||||
notification,
|
notification,
|
||||||
i18n,
|
i18n,
|
||||||
s => s.Server.Indexing);
|
s => s.Server.Indexing);
|
||||||
@ -89,11 +89,11 @@ export class IndexingSettingsComponent extends SettingsComponent<ServerConfig.In
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async index(createThumbnails: boolean) {
|
async index() {
|
||||||
this.inProgress = true;
|
this.inProgress = true;
|
||||||
this.error = '';
|
this.error = '';
|
||||||
try {
|
try {
|
||||||
await this.tasksService.start(DefaultsTasks[DefaultsTasks.Indexing], {createThumbnails: !!createThumbnails});
|
await this.tasksService.start(DefaultsTasks[DefaultsTasks.Indexing]);
|
||||||
this.notification.info(this.i18n('Folder indexing started'));
|
this.notification.info(this.i18n('Folder indexing started'));
|
||||||
this.inProgress = false;
|
this.inProgress = false;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class MapSettingsComponent extends SettingsComponent<ClientConfig.MapConf
|
|||||||
_settingsService: MapSettingsService,
|
_settingsService: MapSettingsService,
|
||||||
notification: NotificationService,
|
notification: NotificationService,
|
||||||
i18n: I18n) {
|
i18n: I18n) {
|
||||||
super(i18n('Map'), _authService, _navigation, <any>_settingsService, notification, i18n, s => s.Client.Map);
|
super(i18n('Map'), _authService, _navigation, _settingsService, notification, i18n, s => s.Client.Map);
|
||||||
|
|
||||||
this.mapProviders = Utils.enumToArray(ClientConfig.MapProviders);
|
this.mapProviders = Utils.enumToArray(ClientConfig.MapProviders);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export class MetaFileSettingsComponent extends SettingsComponent<ClientConfig.Me
|
|||||||
_settingsService: MetaFileSettingsService,
|
_settingsService: MetaFileSettingsService,
|
||||||
notification: NotificationService,
|
notification: NotificationService,
|
||||||
i18n: I18n) {
|
i18n: I18n) {
|
||||||
super(i18n('Meta file'), _authService, _navigation, <any>_settingsService, notification, i18n, s => s.Client.MetaFile);
|
super(i18n('Meta file'), _authService, _navigation, _settingsService, notification, i18n, s => s.Client.MetaFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
[switch-on-text]="text.Enabled"
|
[switch-on-text]="text.Enabled"
|
||||||
[switch-handle-width]="100"
|
[switch-handle-width]="100"
|
||||||
[switch-label-width]="20"
|
[switch-label-width]="20"
|
||||||
[(ngModel)]="settings.Server.enable">
|
[(ngModel)]="settings.Server.enabled">
|
||||||
</bSwitch>
|
</bSwitch>
|
||||||
<small class="form-text text-muted" i18n>Runs directory scanning and thumbnail generation (only for Jimp) in a
|
<small class="form-text text-muted" i18n>Runs directory scanning and thumbnail generation (only for Jimp) in a
|
||||||
different thread
|
different thread
|
||||||
@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group row" [hidden]="simplifiedMode || settings.Server.enable == false">
|
<div class="form-group row" [hidden]="simplifiedMode || settings.Server.enabled == false">
|
||||||
<label class="col-md-2 control-label" for="thumbnailThreads" i18n>Thumbnail threads</label>
|
<label class="col-md-2 control-label" for="thumbnailThreads" i18n>Thumbnail threads</label>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<select id="thumbnailThreads" class="form-control" [(ngModel)]="settings.Server.thumbnailThreads"
|
<select id="thumbnailThreads" class="form-control" [(ngModel)]="settings.Server.thumbnailThreads"
|
||||||
|
|||||||
@ -0,0 +1,4 @@
|
|||||||
|
.buttons-row {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
129
src/frontend/app/ui/settings/photo/photo.settings.component.html
Normal file
129
src/frontend/app/ui/settings/photo/photo.settings.component.html
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<form #settingsForm="ngForm" class="form-horizontal">
|
||||||
|
<div class="card mb-4">
|
||||||
|
<h5 class="card-header">{{Name}}<span *ngIf="changed">*</span></h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
||||||
|
<div [hidden]="settings.photoProcessingLibrary!=PhotoProcessingLib.Jimp"
|
||||||
|
class="alert alert-warning"
|
||||||
|
role="alert" i18n>It is highly recommended to use hardware accelerated (sharp or gm) lib for thumbnail
|
||||||
|
generation
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div [hidden]="simplifiedMode">
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-md-2 control-label" for="lib" i18n>Thumbnail generation library</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<select id="lib" class="form-control" [(ngModel)]="settings.photoProcessingLibrary"
|
||||||
|
name="type" required>
|
||||||
|
<option *ngFor="let type of libTypes" [ngValue]="type.key">{{type.value}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<small *ngIf="settings.photoProcessingLibrary==PhotoProcessingLib.sharp"
|
||||||
|
class="form-text text-muted" i18n>Make sure that sharp node module is installed (npm install sharp).
|
||||||
|
</small>
|
||||||
|
<small *ngIf="settings.photoProcessingLibrary==PhotoProcessingLib.gm"
|
||||||
|
class="form-text text-muted">
|
||||||
|
<ng-container i18n>Make sure that gm node module and</ng-container>
|
||||||
|
<a
|
||||||
|
href="http://www.graphicsmagick.org/" i18n>GraphicsMagick</a>
|
||||||
|
<ng-container i18n>are installed (npm install sharp).</ng-container>
|
||||||
|
</small>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<p class="title" i18n>Photo converting:</p>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-md-2 control-label" for="enablePhotoConverting" i18n>Converting</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<bSwitch
|
||||||
|
id="enablePhotoConverting"
|
||||||
|
class="switch"
|
||||||
|
name="enablePhotoConverting"
|
||||||
|
[switch-on-color]="'primary'"
|
||||||
|
[switch-inverse]="true"
|
||||||
|
[switch-off-text]="text.Disabled"
|
||||||
|
[switch-on-text]="text.Enabled"
|
||||||
|
[switch-handle-width]="100"
|
||||||
|
[switch-label-width]="20"
|
||||||
|
[(ngModel)]="settings.client.Converting.enabled">
|
||||||
|
</bSwitch>
|
||||||
|
<small class="form-text text-muted" i18n>Downsizes photos for faster preview loading. (Zooming in to the photo
|
||||||
|
loads the original)</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row" [hidden]="!settings.client.Converting.enabled">
|
||||||
|
<label class="col-md-2 control-label" for="onTheFlyConverting" i18n>On the fly converting </label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<bSwitch
|
||||||
|
id="onTheFlyConverting"
|
||||||
|
class="switch"
|
||||||
|
name="onTheFlyConverting"
|
||||||
|
[switch-on-color]="'primary'"
|
||||||
|
[switch-inverse]="true"
|
||||||
|
[switch-off-text]="text.Disabled"
|
||||||
|
[switch-on-text]="text.Enabled"
|
||||||
|
[switch-handle-width]="100"
|
||||||
|
[switch-label-width]="20"
|
||||||
|
[(ngModel)]="settings.server.Converting.onTheFly">
|
||||||
|
</bSwitch>
|
||||||
|
<small class="form-text text-muted" i18n>Converts photos on the fly, when they are requested.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row" [hidden]="!settings.client.Converting.enabled">
|
||||||
|
<label class="col-md-2 control-label" for="resolution" i18n>Resolution</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<select id="resolution" class="form-control" [(ngModel)]="settings.server.Converting.resolution"
|
||||||
|
name="resolution" required>
|
||||||
|
<option *ngFor="let resolution of resolutions" [ngValue]="resolution">{{resolution}}px
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<small class="form-text text-muted" i18n>The shorter edge of the converted photo will be scaled down to this,
|
||||||
|
while
|
||||||
|
keeping the aspect ratio.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<button class="btn btn-success float-right"
|
||||||
|
[disabled]="!settingsForm.form.valid || !changed || inProgress"
|
||||||
|
(click)="save()" i18n>Save
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary float-right"
|
||||||
|
[disabled]=" !changed || inProgress"
|
||||||
|
(click)="reset()" i18n>Reset
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div [hidden]="!settings.client.Converting.enabled">
|
||||||
|
<button class="btn btn-success float-left ml-0"
|
||||||
|
*ngIf="Progress == null"
|
||||||
|
[disabled]="inProgress"
|
||||||
|
title="Indexes the folders"
|
||||||
|
i18n-title
|
||||||
|
(click)="convertPhoto()">
|
||||||
|
<ng-container i18n>Convert photos now</ng-container>
|
||||||
|
<span class="oi oi-media-play ml-2"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary float-left ml-0"
|
||||||
|
*ngIf="Progress != null"
|
||||||
|
[disabled]="inProgress || Progress.state !== TaskState.running"
|
||||||
|
(click)="cancelPhotoConverting()" i18n>Cancel converting
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-container *ngIf="Progress != null">
|
||||||
|
<br/>
|
||||||
|
<hr/>
|
||||||
|
<app-settings-tasks-progress [progress]="Progress"></app-settings-tasks-progress>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
105
src/frontend/app/ui/settings/photo/photo.settings.component.ts
Normal file
105
src/frontend/app/ui/settings/photo/photo.settings.component.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import {Component} from '@angular/core';
|
||||||
|
import {PhotoSettingsService} from './photo.settings.service';
|
||||||
|
import {SettingsComponent} from '../_abstract/abstract.settings.component';
|
||||||
|
import {AuthenticationService} from '../../../model/network/authentication.service';
|
||||||
|
import {NavigationService} from '../../../model/navigation.service';
|
||||||
|
import {NotificationService} from '../../../model/notification.service';
|
||||||
|
import {ClientConfig} from '../../../../../common/config/public/ConfigClass';
|
||||||
|
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||||
|
import {ScheduledTasksService} from '../scheduled-tasks.service';
|
||||||
|
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
||||||
|
import {Utils} from '../../../../../common/Utils';
|
||||||
|
import {DefaultsTasks} from '../../../../../common/entities/task/TaskDTO';
|
||||||
|
import {ErrorDTO} from '../../../../../common/entities/Error';
|
||||||
|
import {TaskState} from '../../../../../common/entities/settings/TaskProgressDTO';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-settings-photo',
|
||||||
|
templateUrl: './photo.settings.component.html',
|
||||||
|
styleUrls: ['./photo.settings.component.css',
|
||||||
|
'../_abstract/abstract.settings.component.css'],
|
||||||
|
providers: [PhotoSettingsService],
|
||||||
|
})
|
||||||
|
export class PhotoSettingsComponent extends SettingsComponent<{
|
||||||
|
photoProcessingLibrary: ServerConfig.PhotoProcessingLib,
|
||||||
|
server: ServerConfig.PhotoConfig,
|
||||||
|
client: ClientConfig.PhotoConfig
|
||||||
|
}> {
|
||||||
|
resolutions = [720, 1080, 1440, 2160, 4320];
|
||||||
|
PhotoProcessingLib = ServerConfig.PhotoProcessingLib;
|
||||||
|
TaskState = TaskState;
|
||||||
|
|
||||||
|
libTypes = Utils
|
||||||
|
.enumToArray(ServerConfig.PhotoProcessingLib).map((v) => {
|
||||||
|
if (v.value.toLowerCase() === 'sharp') {
|
||||||
|
v.value += ' ' + this.i18n('(recommended)');
|
||||||
|
} else {
|
||||||
|
v.value += ' ' + this.i18n('(deprecated, will be removed)');
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(_authService: AuthenticationService,
|
||||||
|
_navigation: NavigationService,
|
||||||
|
_settingsService: PhotoSettingsService,
|
||||||
|
public tasksService: ScheduledTasksService,
|
||||||
|
notification: NotificationService,
|
||||||
|
i18n: I18n) {
|
||||||
|
super(i18n('Photo'), _authService, _navigation, _settingsService, notification, i18n, s => ({
|
||||||
|
photoProcessingLibrary: s.Server.Media.photoProcessingLibrary,
|
||||||
|
client: s.Client.Media.Photo,
|
||||||
|
server: s.Server.Media.Photo
|
||||||
|
}));
|
||||||
|
const currentRes = _settingsService.Settings.value.Server.Media.Photo.Converting.resolution;
|
||||||
|
if (this.resolutions.indexOf(currentRes) === -1) {
|
||||||
|
this.resolutions.push(currentRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get Progress() {
|
||||||
|
return this.tasksService.progress.value[DefaultsTasks[DefaultsTasks['Photo Converting']]];
|
||||||
|
}
|
||||||
|
|
||||||
|
async convertPhoto() {
|
||||||
|
this.inProgress = true;
|
||||||
|
this.error = '';
|
||||||
|
try {
|
||||||
|
await this.tasksService.start(DefaultsTasks[DefaultsTasks['Photo Converting']]);
|
||||||
|
this.notification.info(this.i18n('Photo converting started'));
|
||||||
|
this.inProgress = false;
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
if (err.message) {
|
||||||
|
this.error = (<ErrorDTO>err).message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inProgress = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelPhotoConverting() {
|
||||||
|
this.inProgress = true;
|
||||||
|
this.error = '';
|
||||||
|
try {
|
||||||
|
await this.tasksService.stop(DefaultsTasks[DefaultsTasks['Photo Converting']]);
|
||||||
|
this.notification.info(this.i18n('Photo converting interrupted'));
|
||||||
|
this.inProgress = false;
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
if (err.message) {
|
||||||
|
this.error = (<ErrorDTO>err).message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inProgress = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
21
src/frontend/app/ui/settings/photo/photo.settings.service.ts
Normal file
21
src/frontend/app/ui/settings/photo/photo.settings.service.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {NetworkService} from '../../../model/network/network.service';
|
||||||
|
import {ClientConfig} from '../../../../../common/config/public/ConfigClass';
|
||||||
|
import {SettingsService} from '../settings.service';
|
||||||
|
import {AbstractSettingsService} from '../_abstract/abstract.settings.service';
|
||||||
|
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PhotoSettingsService extends AbstractSettingsService<{ server: ServerConfig.PhotoConfig, client: ClientConfig.PhotoConfig }> {
|
||||||
|
constructor(private _networkService: NetworkService,
|
||||||
|
_settingsService: SettingsService) {
|
||||||
|
super(_settingsService);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public updateSettings(settings: { server: ServerConfig.PhotoConfig, client: ClientConfig.PhotoConfig }): Promise<void> {
|
||||||
|
return this._networkService.putJson('/settings/photo', {settings: settings});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -2,7 +2,8 @@
|
|||||||
<div class="card mb-4"
|
<div class="card mb-4"
|
||||||
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
|
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
|
||||||
<h5 class="card-header">
|
<h5 class="card-header">
|
||||||
{{Name}}<ng-container *ngIf="changed">*</ng-container>
|
{{Name}}
|
||||||
|
<ng-container *ngIf="changed">*</ng-container>
|
||||||
<div class="switch-wrapper">
|
<div class="switch-wrapper">
|
||||||
<bSwitch
|
<bSwitch
|
||||||
class="switch"
|
class="switch"
|
||||||
@ -22,7 +23,8 @@
|
|||||||
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
||||||
|
|
||||||
<ng-container *ngIf="settings.enabled || _settingsService.isSupported()">
|
<ng-container *ngIf="settings.enabled || _settingsService.isSupported()">
|
||||||
<div class="panel-info" i18n>
|
|
||||||
|
<div class="alert alert-secondary" role="alert" i18n>
|
||||||
This feature enables you to generate 'random photo' urls.
|
This feature enables you to generate 'random photo' urls.
|
||||||
That URL returns a photo random selected from your gallery.
|
That URL returns a photo random selected from your gallery.
|
||||||
You can use the url with 3rd party application like random changing desktop background.
|
You can use the url with 3rd party application like random changing desktop background.
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export class TasksSettingsComponent extends SettingsComponent<ServerConfig.TaskC
|
|||||||
super(i18n('Tasks'),
|
super(i18n('Tasks'),
|
||||||
_authService,
|
_authService,
|
||||||
_navigation,
|
_navigation,
|
||||||
<any>_settingsService,
|
_settingsService,
|
||||||
notification,
|
notification,
|
||||||
i18n,
|
i18n,
|
||||||
s => s.Server.Tasks);
|
s => s.Server.Tasks);
|
||||||
|
|||||||
@ -5,34 +5,7 @@
|
|||||||
</h5>
|
</h5>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
||||||
<div [hidden]="settings.server.processingLibrary!=ThumbnailProcessingLib.Jimp"
|
|
||||||
class="alert alert-warning"
|
|
||||||
role="alert" i18n>It is highly recommended to use hardware accelerated (sharp or gm) lib for thumbnail
|
|
||||||
generation
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label class="col-md-2 control-label" for="lib" i18n>Thumbnail generation library</label>
|
|
||||||
<div class="col-md-10">
|
|
||||||
<select id="lib" class="form-control" [(ngModel)]="settings.server.processingLibrary"
|
|
||||||
name="type" required>
|
|
||||||
<option *ngFor="let type of types" [ngValue]="type.key">{{type.value}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<small *ngIf="settings.server.processingLibrary==ThumbnailProcessingLib.sharp"
|
|
||||||
class="form-text text-muted" i18n>Make sure that sharp node module is installed (npm install sharp).
|
|
||||||
</small>
|
|
||||||
<small *ngIf="settings.server.processingLibrary==ThumbnailProcessingLib.gm"
|
|
||||||
class="form-text text-muted">
|
|
||||||
<ng-container i18n>Make sure that gm node module and</ng-container>
|
|
||||||
<a
|
|
||||||
href="http://www.graphicsmagick.org/" i18n>GraphicsMagick</a>
|
|
||||||
<ng-container i18n>are installed (npm install sharp).</ng-container>
|
|
||||||
</small>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row" [hidden]="simplifiedMode">
|
<div class="form-group row" [hidden]="simplifiedMode">
|
||||||
<label class="col-md-2 control-label" for="quality" i18n>Thumbnail Quality</label>
|
<label class="col-md-2 control-label" for="quality" i18n>Thumbnail Quality</label>
|
||||||
@ -84,7 +57,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<button class="btn btn-success float-right"
|
<button class="btn btn-success float-right"
|
||||||
[disabled]="!settingsForm.form.valid || !changed || inProgress"
|
[disabled]="!settingsForm.form.valid || !changed || inProgress"
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'
|
|||||||
export class ThumbnailSettingsComponent
|
export class ThumbnailSettingsComponent
|
||||||
extends SettingsComponent<{ server: ServerConfig.ThumbnailConfig, client: ClientConfig.ThumbnailConfig }>
|
extends SettingsComponent<{ server: ServerConfig.ThumbnailConfig, client: ClientConfig.ThumbnailConfig }>
|
||||||
implements OnInit {
|
implements OnInit {
|
||||||
types: Array<any> = [];
|
|
||||||
ThumbnailProcessingLib: any;
|
ThumbnailProcessingLib: any;
|
||||||
|
|
||||||
constructor(_authService: AuthenticationService,
|
constructor(_authService: AuthenticationService,
|
||||||
@ -47,16 +46,6 @@ export class ThumbnailSettingsComponent
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.types = Utils
|
|
||||||
.enumToArray(ServerConfig.ThumbnailProcessingLib).map((v) => {
|
|
||||||
if (v.value.toLowerCase() === 'sharp') {
|
|
||||||
v.value += ' ' + this.i18n('(recommended)');
|
|
||||||
} else {
|
|
||||||
v.value += ' ' + this.i18n('(deprecated, will be removed)');
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
});
|
|
||||||
this.ThumbnailProcessingLib = ServerConfig.ThumbnailProcessingLib;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,8 @@ export class UserMangerSettingsComponent implements OnInit, ISettingsComponent {
|
|||||||
public error: string = null;
|
public error: string = null;
|
||||||
public inProgress = false;
|
public inProgress = false;
|
||||||
Name: string;
|
Name: string;
|
||||||
hasAvailableSettings = true;
|
HasAvailableSettings = true;
|
||||||
|
Changed = false;
|
||||||
|
|
||||||
|
|
||||||
text = {
|
text = {
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
<form #settingsForm="ngForm" class="form-horizontal">
|
<form #settingsForm="ngForm" class="form-horizontal">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<h5 class="card-header">
|
<h5 class="card-header">
|
||||||
{{Name}}<ng-container *ngIf="changed">*</ng-container>
|
{{Name}}
|
||||||
|
<ng-container *ngIf="changed">*</ng-container>
|
||||||
<div class="switch-wrapper">
|
<div class="switch-wrapper">
|
||||||
<bSwitch
|
<bSwitch
|
||||||
class="switch"
|
class="switch"
|
||||||
@ -20,13 +21,27 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
||||||
|
|
||||||
<ng-container i18n>Video support uses ffmpeg. ffmpeg and ffprobe binaries need to be available in the PATH or the
|
<div class="alert alert-secondary" role="alert">
|
||||||
|
<ng-container i18n>Video support uses ffmpeg. ffmpeg and ffprobe binaries need to be available in the PATH or
|
||||||
|
the
|
||||||
@ffmpeg-installer/ffmpeg and @ffprobe-installer/ffprobe optional node packages need to be installed.
|
@ffmpeg-installer/ffmpeg and @ffprobe-installer/ffprobe optional node packages need to be installed.
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
<p class="title" i18n>Video transcoding:</p>
|
<p class="title" i18n>Video transcoding:</p>
|
||||||
|
<div class="alert alert-secondary" role="alert">
|
||||||
|
<ng-container i18n>To ensure smooth video playback, video transcoding is recommended to a lower bit rate than
|
||||||
|
the
|
||||||
|
server's upload rate.
|
||||||
|
</ng-container>
|
||||||
|
<ng-container i18n>The transcoded videos will be save to the thumbnail folder.</ng-container>
|
||||||
|
<ng-container i18n>You can trigger the transcoding manually, but you can also create an automatic encoding task
|
||||||
|
in
|
||||||
|
advanced settings mode.
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group row" [hidden]="simplifiedMode">
|
<div class="form-group row" [hidden]="simplifiedMode">
|
||||||
<label class="col-md-2 control-label" for="format" i18n>Format</label>
|
<label class="col-md-2 control-label" for="format" i18n>Format</label>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
@ -109,28 +124,17 @@
|
|||||||
(click)="reset()" i18n>Reset
|
(click)="reset()" i18n>Reset
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<br/>
|
|
||||||
<hr/>
|
|
||||||
|
|
||||||
<ng-container i18n>To ensure smooth video playback, video transcoding is recommended to a lower bit rate than the
|
<button class="btn btn-success float-left ml-0"
|
||||||
server's upload rate.
|
|
||||||
</ng-container>
|
|
||||||
<ng-container i18n>The transcoded videos will be save to the thumbnail folder.</ng-container>
|
|
||||||
<ng-container i18n>You can trigger the transcoding manually, but you can also create an automatic encoding task in
|
|
||||||
advanced settings mode.
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-success float-right"
|
|
||||||
*ngIf="Progress == null"
|
*ngIf="Progress == null"
|
||||||
[disabled]="inProgress"
|
[disabled]="inProgress"
|
||||||
title="Indexes the folders"
|
title="Indexes the folders"
|
||||||
i18n-title
|
i18n-title
|
||||||
(click)="transcode()" i18n>Transcode videos now
|
(click)="transcode()">
|
||||||
|
<ng-container i18n>Transcode videos now</ng-container>
|
||||||
|
<span class="oi oi-media-play ml-2"></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-secondary float-right"
|
<button class="btn btn-secondary float-left ml-0"
|
||||||
*ngIf="Progress != null"
|
*ngIf="Progress != null"
|
||||||
[disabled]="inProgress || Progress.state !== TaskState.running"
|
[disabled]="inProgress || Progress.state !== TaskState.running"
|
||||||
(click)="cancelTranscoding()" i18n>Cancel transcoding
|
(click)="cancelTranscoding()" i18n>Cancel transcoding
|
||||||
|
|||||||
@ -35,10 +35,15 @@ export class VideoSettingsComponent extends SettingsComponent<{ server: ServerCo
|
|||||||
public tasksService: ScheduledTasksService,
|
public tasksService: ScheduledTasksService,
|
||||||
notification: NotificationService,
|
notification: NotificationService,
|
||||||
i18n: I18n) {
|
i18n: I18n) {
|
||||||
super(i18n('Video'), _authService, _navigation, <any>_settingsService, notification, i18n, s => ({
|
super(i18n('Video'), _authService, _navigation, _settingsService, notification, i18n, s => ({
|
||||||
client: s.Client.Media.Video,
|
client: s.Client.Media.Video,
|
||||||
server: s.Server.Media.Video
|
server: s.Server.Media.Video
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const currentRes = _settingsService.Settings.value.Server.Media.Video.transcoding.resolution;
|
||||||
|
if (this.resolutions.indexOf(currentRes) === -1) {
|
||||||
|
this.resolutions.push(currentRes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,16 +3,17 @@ import {NetworkService} from '../../../model/network/network.service';
|
|||||||
import {ClientConfig} from '../../../../../common/config/public/ConfigClass';
|
import {ClientConfig} from '../../../../../common/config/public/ConfigClass';
|
||||||
import {SettingsService} from '../settings.service';
|
import {SettingsService} from '../settings.service';
|
||||||
import {AbstractSettingsService} from '../_abstract/abstract.settings.service';
|
import {AbstractSettingsService} from '../_abstract/abstract.settings.service';
|
||||||
|
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VideoSettingsService extends AbstractSettingsService<ClientConfig.MapConfig> {
|
export class VideoSettingsService extends AbstractSettingsService<{ server: ServerConfig.VideoConfig, client: ClientConfig.VideoConfig }> {
|
||||||
constructor(private _networkService: NetworkService,
|
constructor(private _networkService: NetworkService,
|
||||||
_settingsService: SettingsService) {
|
_settingsService: SettingsService) {
|
||||||
super(_settingsService);
|
super(_settingsService);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateSettings(settings: ClientConfig.VideoConfig): Promise<void> {
|
public updateSettings(settings: { server: ServerConfig.VideoConfig, client: ClientConfig.VideoConfig }): Promise<void> {
|
||||||
return this._networkService.putJson('/settings/video', {settings: settings});
|
return this._networkService.putJson('/settings/video', {settings: settings});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user