1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-02-15 14:03:35 +02:00

improving video support

This commit is contained in:
Patrik J. Braun 2018-11-18 20:26:29 +01:00
parent 4a7e91ceb3
commit ad7225c62e
26 changed files with 331 additions and 67 deletions

View File

@ -5,7 +5,7 @@ import {Logger} from '../Logger';
import {SQLConnection} from '../model/sql/SQLConnection'; import {SQLConnection} from '../model/sql/SQLConnection';
import {DataBaseConfig, DatabaseType, IndexingConfig, IPrivateConfig, ThumbnailConfig} from '../../common/config/private/IPrivateConfig'; import {DataBaseConfig, DatabaseType, IndexingConfig, IPrivateConfig, ThumbnailConfig} from '../../common/config/private/IPrivateConfig';
import {Config} from '../../common/config/private/Config'; import {Config} from '../../common/config/private/Config';
import {ConfigDiagnostics} from '../model/ConfigDiagnostics'; import {ConfigDiagnostics} from '../model/diagnostics/ConfigDiagnostics';
import {ClientConfig} from '../../common/config/public/ConfigClass'; import {ClientConfig} from '../../common/config/public/ConfigClass';
import {BasicConfigDTO} from '../../common/entities/settings/BasicConfigDTO'; import {BasicConfigDTO} from '../../common/entities/settings/BasicConfigDTO';
import {OtherConfigDTO} from '../../common/entities/settings/OtherConfigDTO'; import {OtherConfigDTO} from '../../common/entities/settings/OtherConfigDTO';
@ -80,6 +80,27 @@ export class AdminMWs {
return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err));
} }
} }
public static async updateVideoSettings(req: Request, res: Response, next: NextFunction) {
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed'));
}
try {
await ConfigDiagnostics.testVideoConfig(<ClientConfig.VideoConfig>req.body.settings);
Config.Client.Video = <ClientConfig.VideoConfig>req.body.settings;
// only updating explicitly set config (not saving config set by the diagnostics)
const original = Config.original();
original.Client.Video = <ClientConfig.VideoConfig>req.body.settings;
original.save();
await ConfigDiagnostics.runDiagnostics();
Logger.info(LOG_TAG, 'new config:');
Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t'));
return next();
} catch (err) {
return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err));
}
}
public static async updateShareSettings(req: Request, res: Response, next: NextFunction) { public static async updateShareSettings(req: Request, res: Response, next: NextFunction) {
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {

View File

@ -52,7 +52,7 @@ export class GalleryMWs {
} }
public static removeCyclicDirectoryReferences(req: Request, res: Response, next: NextFunction) { public static cleanUpGalleryResults(req: Request, res: Response, next: NextFunction) {
if (!req.resultPipe) { if (!req.resultPipe) {
return next(); return next();
} }
@ -61,17 +61,6 @@ export class GalleryMWs {
if (cw.notModified === true) { if (cw.notModified === true) {
return next(); return next();
} }
const removeDirs = (dir: DirectoryDTO) => {
dir.media.forEach((photo: PhotoDTO) => {
photo.directory = null;
});
dir.directories.forEach((directory: DirectoryDTO) => {
removeDirs(directory);
directory.parent = null;
});
};
const cleanUpMedia = (media: MediaDTO[]) => { const cleanUpMedia = (media: MediaDTO[]) => {
media.forEach(m => { media.forEach(m => {
@ -89,7 +78,7 @@ export class GalleryMWs {
}; };
if (cw.directory) { if (cw.directory) {
removeDirs(cw.directory); DirectoryDTO.removeReferences(cw.directory);
// TODO: remove when typeorm inheritance is fixed // TODO: remove when typeorm inheritance is fixed
cleanUpMedia(cw.directory.media); cleanUpMedia(cw.directory.media);
} }
@ -98,6 +87,19 @@ export class GalleryMWs {
} }
if (Config.Client.Video.enabled === false) {
if (cw.directory) {
const removeVideos = (dir: DirectoryDTO) => {
dir.media = dir.media.filter(m => !MediaDTO.isVideo(m));
dir.directories.forEach(d => removeVideos(d));
};
removeVideos(cw.directory);
}
if (cw.searchResult) {
cw.searchResult.media = cw.searchResult.media.filter(m => !MediaDTO.isVideo(m));
}
}
return next(); return next();
} }

View File

@ -0,0 +1,13 @@
export class FFmpegFactory {
public static get() {
const ffmpeg = require('fluent-ffmpeg');
try {
const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
ffmpeg.setFfmpegPath(ffmpegPath);
const ffprobePath = require('@ffprobe-installer/ffprobe').path;
ffmpeg.setFfprobePath(ffprobePath);
} catch (e) {
}
return ffmpeg;
}
}

View File

@ -1,17 +1,19 @@
import {Config} from '../../common/config/private/Config'; import {Config} from '../../../common/config/private/Config';
import { import {
DataBaseConfig, DataBaseConfig,
DatabaseType, DatabaseType,
IPrivateConfig, IPrivateConfig,
ThumbnailConfig, ThumbnailConfig,
ThumbnailProcessingLib ThumbnailProcessingLib
} from '../../common/config/private/IPrivateConfig'; } from '../../../common/config/private/IPrivateConfig';
import {Logger} from '../Logger'; import {Logger} from '../../Logger';
import {NotificationManager} from './NotifocationManager'; import {NotificationManager} from '../NotifocationManager';
import {ProjectPath} from '../ProjectPath'; import {ProjectPath} from '../../ProjectPath';
import {SQLConnection} from './sql/SQLConnection'; import {SQLConnection} from '../sql/SQLConnection';
import * as fs from 'fs'; import * as fs from 'fs';
import {ClientConfig} from '../../common/config/public/ConfigClass'; import {ClientConfig} from '../../../common/config/public/ConfigClass';
import VideoConfig = ClientConfig.VideoConfig;
import {FFmpegFactory} from '../FFmpegFactory';
const LOG_TAG = '[ConfigDiagnostics]'; const LOG_TAG = '[ConfigDiagnostics]';
@ -24,6 +26,31 @@ export class ConfigDiagnostics {
} }
static testVideoConfig(videoConfig: VideoConfig) {
return new Promise((resolve, reject) => {
try {
if (videoConfig.enabled === true) {
const ffmpeg = FFmpegFactory.get();
ffmpeg().getAvailableCodecs((err) => {
if (err) {
return reject(new Error('Error accessing ffmpeg, cant find executable: ' + err.toString()));
}
ffmpeg(__dirname + '/blank.jpg').ffprobe((err2) => {
if (err2) {
return reject(new Error('Error accessing ffmpeg-probe, cant find executable: ' + err2.toString()));
}
return resolve();
});
});
} else {
return resolve();
}
} catch (e) {
return reject(new Error('unkown video error: ' + e.toString()));
}
});
}
static async testThumbnailLib(processingLibrary: ThumbnailProcessingLib) { static async testThumbnailLib(processingLibrary: ThumbnailProcessingLib) {
switch (processingLibrary) { switch (processingLibrary) {
case ThumbnailProcessingLib.sharp: case ThumbnailProcessingLib.sharp:
@ -44,8 +71,8 @@ export class ConfigDiagnostics {
} }
} }
static async testThumbnailFolder(folder: string) { static testThumbnailFolder(folder: string) {
await new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.access(folder, fs.constants.W_OK, (err) => { fs.access(folder, fs.constants.W_OK, (err) => {
if (err) { if (err) {
reject({message: 'Error during getting write access to temp folder', error: err.toString()}); reject({message: 'Error during getting write access to temp folder', error: err.toString()});
@ -55,8 +82,8 @@ export class ConfigDiagnostics {
}); });
} }
static async testImageFolder(folder: string) { static testImageFolder(folder: string) {
await new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!fs.existsSync(folder)) { if (!fs.existsSync(folder)) {
reject('Images folder not exists: \'' + folder + '\''); reject('Images folder not exists: \'' + folder + '\'');
} }
@ -164,6 +191,16 @@ export class ConfigDiagnostics {
} }
try {
await ConfigDiagnostics.testVideoConfig(Config.Client.Video);
} catch (ex) {
const err: Error = ex;
NotificationManager.warning('Video support error, switching off..', err.toString());
Logger.warn(LOG_TAG, 'Video support error, switching off..', err.toString());
Config.Client.Video.enabled = false;
}
try { try {
await ConfigDiagnostics.testImageFolder(Config.Server.imagesFolder); await ConfigDiagnostics.testImageFolder(Config.Server.imagesFolder);
} catch (ex) { } catch (ex) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

View File

@ -17,7 +17,7 @@ import {VideoEntity} from './enitites/VideoEntity';
export class SQLConnection { export class SQLConnection {
private static VERSION = 1; private static VERSION = 2;
constructor() { constructor() {
} }

View File

@ -5,15 +5,17 @@ import {CameraMetadata, GPSMetadata, PhotoDTO, PhotoMetadata} from '../../../com
import {Logger} from '../../Logger'; import {Logger} from '../../Logger';
import {IptcParser} from 'ts-node-iptc'; import {IptcParser} from 'ts-node-iptc';
import {ExifParserFactory, OrientationTypes} from 'ts-exif-parser'; import {ExifParserFactory, OrientationTypes} from 'ts-exif-parser';
import * as ffmpeg from 'fluent-ffmpeg';
import {FfprobeData} from 'fluent-ffmpeg'; import {FfprobeData} from 'fluent-ffmpeg';
import {ProjectPath} from '../../ProjectPath'; import {ProjectPath} from '../../ProjectPath';
import {Config} from '../../../common/config/private/Config'; import {Config} from '../../../common/config/private/Config';
import {VideoDTO, VideoMetadata} from '../../../common/entities/VideoDTO'; import {VideoDTO, VideoMetadata} from '../../../common/entities/VideoDTO';
import {MediaDimension, MediaMetadata} from '../../../common/entities/MediaDTO'; import {MediaDimension} from '../../../common/entities/MediaDTO';
import {FFmpegFactory} from '../FFmpegFactory';
const LOG_TAG = '[DiskManagerTask]'; const LOG_TAG = '[DiskManagerTask]';
const ffmpeg = FFmpegFactory.get();
export class DiskMangerWorker { export class DiskMangerWorker {
private static isImage(fullPath: string) { private static isImage(fullPath: string) {
const extensions = [ const extensions = [
@ -33,8 +35,7 @@ export class DiskMangerWorker {
private static isVideo(fullPath: string) { private static isVideo(fullPath: string) {
const extensions = [ const extensions = [
'.mp4', '.mp4'
'.webm'
]; ];
const extension = path.extname(fullPath).toLowerCase(); const extension = path.extname(fullPath).toLowerCase();
@ -83,7 +84,8 @@ export class DiskMangerWorker {
if (maxPhotos != null && directory.media.length > maxPhotos) { if (maxPhotos != null && directory.media.length > maxPhotos) {
break; break;
} }
} else if (DiskMangerWorker.isVideo(fullFilePath)) { } else if (Config.Client.Video.enabled === true &&
DiskMangerWorker.isVideo(fullFilePath)) {
directory.media.push(<VideoDTO>{ directory.media.push(<VideoDTO>{
name: file, name: file,
directory: null, directory: null,
@ -98,7 +100,7 @@ export class DiskMangerWorker {
return resolve(directory); return resolve(directory);
} catch (err) { } catch (err) {
return reject({error: err}); return reject({error: err.toString()});
} }
}); });

View File

@ -3,6 +3,7 @@ import {Dimensions, State} from 'gm';
import {Logger} from '../../Logger'; import {Logger} from '../../Logger';
import {FfmpegCommand, FfprobeData} from 'fluent-ffmpeg'; import {FfmpegCommand, FfprobeData} from 'fluent-ffmpeg';
import {ThumbnailProcessingLib} from '../../../common/config/private/IPrivateConfig'; import {ThumbnailProcessingLib} from '../../../common/config/private/IPrivateConfig';
import {FFmpegFactory} from '../FFmpegFactory';
export class ThumbnailWorker { export class ThumbnailWorker {
@ -50,7 +51,7 @@ export interface RendererInput {
export class VideoRendererFactory { export class VideoRendererFactory {
public static build(): (input: RendererInput) => Promise<void> { public static build(): (input: RendererInput) => Promise<void> {
const ffmpeg = require('fluent-ffmpeg'); const ffmpeg = FFmpegFactory.get();
return (input: RendererInput): Promise<void> => { return (input: RendererInput): Promise<void> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -59,6 +59,12 @@ export class AdminRouter {
AdminMWs.updateMapSettings, AdminMWs.updateMapSettings,
RenderingMWs.renderOK RenderingMWs.renderOK
); );
app.put('/api/settings/video',
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
AdminMWs.updateVideoSettings,
RenderingMWs.renderOK
);
app.put('/api/settings/authentication', app.put('/api/settings/authentication',
AuthenticationMWs.authenticate, AuthenticationMWs.authenticate,

View File

@ -27,7 +27,7 @@ export class GalleryRouter {
AuthenticationMWs.authoriseDirectory, AuthenticationMWs.authoriseDirectory,
GalleryMWs.listDirectory, GalleryMWs.listDirectory,
ThumbnailGeneratorMWs.addThumbnailInformation, ThumbnailGeneratorMWs.addThumbnailInformation,
GalleryMWs.removeCyclicDirectoryReferences, GalleryMWs.cleanUpGalleryResults,
RenderingMWs.renderResult RenderingMWs.renderResult
); );
} }
@ -98,7 +98,7 @@ export class GalleryRouter {
AuthenticationMWs.authorise(UserRoles.Guest), AuthenticationMWs.authorise(UserRoles.Guest),
GalleryMWs.search, GalleryMWs.search,
ThumbnailGeneratorMWs.addThumbnailInformation, ThumbnailGeneratorMWs.addThumbnailInformation,
GalleryMWs.removeCyclicDirectoryReferences, GalleryMWs.cleanUpGalleryResults,
RenderingMWs.renderResult RenderingMWs.renderResult
); );
} }
@ -109,7 +109,7 @@ export class GalleryRouter {
AuthenticationMWs.authorise(UserRoles.Guest), AuthenticationMWs.authorise(UserRoles.Guest),
GalleryMWs.instantSearch, GalleryMWs.instantSearch,
ThumbnailGeneratorMWs.addThumbnailInformation, ThumbnailGeneratorMWs.addThumbnailInformation,
GalleryMWs.removeCyclicDirectoryReferences, GalleryMWs.cleanUpGalleryResults,
RenderingMWs.renderResult RenderingMWs.renderResult
); );
} }

View File

@ -17,7 +17,7 @@ import {LoggerRouter} from './routes/LoggerRouter';
import {ThumbnailGeneratorMWs} from './middlewares/thumbnail/ThumbnailGeneratorMWs'; import {ThumbnailGeneratorMWs} from './middlewares/thumbnail/ThumbnailGeneratorMWs';
import {DiskManager} from './model/DiskManger'; import {DiskManager} from './model/DiskManger';
import {NotificationRouter} from './routes/NotificationRouter'; import {NotificationRouter} from './routes/NotificationRouter';
import {ConfigDiagnostics} from './model/ConfigDiagnostics'; import {ConfigDiagnostics} from './model/diagnostics/ConfigDiagnostics';
import {Localizations} from './model/Localizations'; import {Localizations} from './model/Localizations';
import {CookieNames} from '../common/CookieNames'; import {CookieNames} from '../common/CookieNames';

View File

@ -43,6 +43,11 @@ export module ClientConfig {
NavBar: NavBarConfig; NavBar: NavBarConfig;
} }
export interface VideoConfig {
enabled: boolean;
}
export interface Config { export interface Config {
applicationTitle: string; applicationTitle: string;
publicUrl: string; publicUrl: string;
@ -55,6 +60,7 @@ export module ClientConfig {
Other: OtherConfig; Other: OtherConfig;
authenticationRequired: boolean; authenticationRequired: boolean;
languages: string[]; languages: string[];
Video: VideoConfig;
} }
} }
@ -91,6 +97,9 @@ export class PublicConfigClass {
RandomPhoto: { RandomPhoto: {
enabled: true enabled: true
}, },
Video: {
enabled: true
},
Other: { Other: {
enableCache: true, enableCache: true,
enableOnScrollRendering: true, enableOnScrollRendering: true,

View File

@ -1,4 +1,5 @@
import {MediaDTO} from './MediaDTO'; import {MediaDTO} from './MediaDTO';
import {PhotoDTO} from './PhotoDTO';
export interface DirectoryDTO { export interface DirectoryDTO {
id: number; id: number;
@ -23,4 +24,16 @@ export module DirectoryDTO {
directory.parent = dir; directory.parent = dir;
}); });
}; };
export const removeReferences = (dir: DirectoryDTO) => {
dir.media.forEach((photo: PhotoDTO) => {
photo.directory = null;
});
dir.directories.forEach((directory: DirectoryDTO) => {
removeReferences(directory);
directory.parent = null;
});
};
} }

View File

@ -43,13 +43,15 @@
<app-settings-database [simplifiedMode]="simplifiedMode"></app-settings-database> <app-settings-database [simplifiedMode]="simplifiedMode"></app-settings-database>
<app-settings-thumbnail #thumbnail [hidden]="!thumbnail.hasAvailableSettings" <app-settings-thumbnail #thumbnail [hidden]="!thumbnail.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-thumbnail> [simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
<app-settings-video #video [hidden]="!video.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-video>
<app-settings-search #search [hidden]="!search.hasAvailableSettings" <app-settings-search #search [hidden]="!search.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-search> [simplifiedMode]="simplifiedMode"></app-settings-search>
<app-settings-share #share [hidden]="!share.hasAvailableSettings" <app-settings-share #share [hidden]="!share.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-share> [simplifiedMode]="simplifiedMode"></app-settings-share>
<app-settings-map #map [hidden]="!map.hasAvailableSettings" [simplifiedMode]="simplifiedMode"></app-settings-map> <app-settings-map #map [hidden]="!map.hasAvailableSettings" [simplifiedMode]="simplifiedMode"></app-settings-map>
<app-settings-random-photo #random [hidden]="!random.hasAvailableSettings" <app-settings-random-photo #random [hidden]="!random.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-random-photo> [simplifiedMode]="simplifiedMode"></app-settings-random-photo>
<app-settings-other #other [hidden]="!other.hasAvailableSettings" <app-settings-other #other [hidden]="!other.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-other> [simplifiedMode]="simplifiedMode"></app-settings-other>
<app-settings-indexing #indexing [hidden]="!indexing.hasAvailableSettings" <app-settings-indexing #indexing [hidden]="!indexing.hasAvailableSettings"

View File

@ -73,6 +73,7 @@ import {StringifySortingMethod} from './pipes/StringifySortingMethod';
import {RandomQueryBuilderGalleryComponent} from './gallery/random-query-builder/random-query-builder.gallery.component'; import {RandomQueryBuilderGalleryComponent} from './gallery/random-query-builder/random-query-builder.gallery.component';
import {RandomPhotoSettingsComponent} from './settings/random-photo/random-photo.settings.component'; import {RandomPhotoSettingsComponent} from './settings/random-photo/random-photo.settings.component';
import {FixOrientationPipe} from './gallery/FixOrientationPipe'; import {FixOrientationPipe} from './gallery/FixOrientationPipe';
import {VideoSettingsComponent} from './settings/video/video.settings.component';
@Injectable() @Injectable()
export class GoogleMapsConfig { export class GoogleMapsConfig {
@ -159,6 +160,7 @@ export function translationsFactory(locale: string) {
DatabaseSettingsComponent, DatabaseSettingsComponent,
MapSettingsComponent, MapSettingsComponent,
ThumbnailSettingsComponent, ThumbnailSettingsComponent,
VideoSettingsComponent,
SearchSettingsComponent, SearchSettingsComponent,
ShareSettingsComponent, ShareSettingsComponent,
RandomPhotoSettingsComponent, RandomPhotoSettingsComponent,

View File

@ -135,7 +135,7 @@ export abstract class SettingsComponent<T, S extends AbstractSettingsService<T>
try { try {
await this._settingsService.updateSettings(this.settings); await this._settingsService.updateSettings(this.settings);
await this.getSettings(); await this.getSettings();
this.notification.success(this.name + this.i18n(' settings saved'), this.i18n('Success')); this.notification.success(this.name + ' ' + this.i18n('settings saved'), this.i18n('Success'));
this.inProgress = false; this.inProgress = false;
return true; return true;
} catch (err) { } catch (err) {

View File

@ -16,9 +16,9 @@
<ng-container *ngIf="settings.type == DatabaseType.mysql"> <ng-container *ngIf="settings.type == DatabaseType.mysql">
<p class="title" i18n>MySQL settings:</p> <p class="title" i18n>MySQL settings:</p>
<input type="text" class="form-control" i18n-placeholder placeholder="Host" autofocus <input type="text" class="form-control" i18n-placeholder placeholder="Host"
[(ngModel)]="settings.mysql.host" name="host" required> [(ngModel)]="settings.mysql.host" name="host" required>
<input type="text" class="form-control" i18n-placeholder placeholder="Database" autofocus <input type="text" class="form-control" i18n-placeholder placeholder="Database"
[(ngModel)]="settings.mysql.database" name="database" required> [(ngModel)]="settings.mysql.database" name="database" required>
<input type="text" class="form-control" i18n-placeholder placeholder="Username" <input type="text" class="form-control" i18n-placeholder placeholder="Username"
[(ngModel)]="settings.mysql.username" name="username"> [(ngModel)]="settings.mysql.username" name="username">

View File

@ -36,6 +36,9 @@ export class SettingsService {
RandomPhoto: { RandomPhoto: {
enabled: true enabled: true
}, },
Video: {
enabled: true
},
Other: { Other: {
enableCache: true, enableCache: true,
enableOnScrollRendering: true, enableOnScrollRendering: true,

View File

@ -74,7 +74,7 @@
</div> </div>
<form #NewUserForm="ngForm"> <form #NewUserForm="ngForm">
<div class="modal-body"> <div class="modal-body">
<input type="text" class="form-control" i18n-placeholder placeholder="Username" autofocus <input type="text" class="form-control" i18n-placeholder placeholder="Username"
[(ngModel)]="newUser.name" name="name" required> [(ngModel)]="newUser.name" name="name" required>
<input type="password" class="form-control" i18n-placeholder placeholder="Password" <input type="password" class="form-control" i18n-placeholder placeholder="Password"
[(ngModel)]="newUser.password" name="password" required> [(ngModel)]="newUser.password" name="password" required>

View File

@ -0,0 +1,36 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Video settings</ng-container>
<div class="switch-wrapper">
<bSwitch
class="switch"
name="enabled"
[switch-on-color]="'success'"
[switch-inverse]="'inverse'"
[switch-off-text]="text.Disabled"
[switch-on-text]="text.Enabled"
[switch-disabled]="inProgress"
[switch-handle-width]="'100'"
[switch-label-width]="'20'"
[(ngModel)]="settings.enabled">
</bSwitch>
</div>
</h5>
<div class="card-body">
<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 @ffmpeg-installer/ffmpeg and @ffprobe-installer/ffprobe optional node packages need to be installed.</ng-container>&nbsp;
<button class="btn btn-success float-right"
[disabled]="!settingsForm.form.valid || !changed || inProgress"
(click)="save()" i18n>Save
</button>
<button class="btn btn-default float-right"
(click)="reset()" i18n>Reset
</button>
</div>
</div>
</form>

View File

@ -0,0 +1,32 @@
import {Component} from '@angular/core';
import {VideoSettingsService} from './video.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';
@Component({
selector: 'app-settings-video',
templateUrl: './video.settings.component.html',
styleUrls: ['./video.settings.component.css',
'./../_abstract/abstract.settings.component.css'],
providers: [VideoSettingsService],
})
export class VideoSettingsComponent extends SettingsComponent<ClientConfig.VideoConfig> {
constructor(_authService: AuthenticationService,
_navigation: NavigationService,
_settingsService: VideoSettingsService,
notification: NotificationService,
i18n: I18n) {
super(i18n('Video'), _authService, _navigation, <any>_settingsService, notification, i18n, s => s.Client.Video);
}
}

View File

@ -0,0 +1,19 @@
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';
@Injectable()
export class VideoSettingsService extends AbstractSettingsService<ClientConfig.MapConfig> {
constructor(private _networkService: NetworkService,
_settingsService: SettingsService) {
super(_settingsService);
}
public updateSettings(settings: ClientConfig.VideoConfig): Promise<void> {
return this._networkService.putJson('/settings/video', {settings: settings});
}
}

View File

@ -104,7 +104,7 @@
<source>download</source> <source>download</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">25</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
<target>download</target> <target>download</target>
</trans-unit> </trans-unit>
@ -112,7 +112,7 @@
<source>info</source> <source>info</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">29</context> <context context-type="linenumber">31</context>
</context-group> </context-group>
<target>info</target> <target>info</target>
</trans-unit> </trans-unit>
@ -120,11 +120,11 @@
<source>toggle fullscreen</source> <source>toggle fullscreen</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">36</context> <context context-type="linenumber">38</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">45</context> <context context-type="linenumber">47</context>
</context-group> </context-group>
<target>toggle fullscreen</target> <target>toggle fullscreen</target>
</trans-unit> </trans-unit>
@ -132,7 +132,7 @@
<source>close</source> <source>close</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">52</context> <context context-type="linenumber">54</context>
</context-group> </context-group>
<target>close</target> <target>close</target>
</trans-unit> </trans-unit>
@ -140,7 +140,7 @@
<source>key: left arrow</source> <source>key: left arrow</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">60</context> <context context-type="linenumber">68</context>
</context-group> </context-group>
<target>key: left arrow</target> <target>key: left arrow</target>
</trans-unit> </trans-unit>
@ -148,7 +148,7 @@
<source>key: right arrow</source> <source>key: right arrow</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">64</context> <context context-type="linenumber">72</context>
</context-group> </context-group>
<target>key: right arrow</target> <target>key: right arrow</target>
</trans-unit> </trans-unit>
@ -607,6 +607,10 @@
<context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context> <context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context>
<context context-type="linenumber">104</context> <context context-type="linenumber">104</context>
</context-group> </context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/settings/video/video.settings.component.html</context>
<context context-type="linenumber">28</context>
</context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/settings/search/search.settings.component.html</context> <context context-type="sourcefile">app/settings/search/search.settings.component.html</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">73</context>
@ -640,6 +644,10 @@
<context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context> <context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context>
<context context-type="linenumber">107</context> <context context-type="linenumber">107</context>
</context-group> </context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/settings/video/video.settings.component.html</context>
<context context-type="linenumber">31</context>
</context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/settings/search/search.settings.component.html</context> <context context-type="sourcefile">app/settings/search/search.settings.component.html</context>
<context context-type="linenumber">76</context> <context context-type="linenumber">76</context>
@ -796,6 +804,22 @@
</context-group> </context-group>
<target>';' separated integers. If size is 200, that thumbnail will have 200^2 pixels.</target> <target>';' separated integers. If size is 200, that thumbnail will have 200^2 pixels.</target>
</trans-unit> </trans-unit>
<trans-unit id="b5398623f87ee72ed23f5023918db1707771e925" datatype="html">
<source>Video settings</source>
<context-group purpose="location">
<context context-type="sourcefile">app/settings/video/video.settings.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
<target>Video settings</target>
</trans-unit>
<trans-unit id="6b8862cecf0fd1c8fc1b503e131bfa230d851343" datatype="html">
<source>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.</source>
<context-group purpose="location">
<context context-type="sourcefile">app/settings/video/video.settings.component.html</context>
<context context-type="linenumber">23</context>
</context-group>
<target>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.</target>
</trans-unit>
<trans-unit id="2e75ae3885931555902da6b288ed616843d5dc3c" datatype="html"> <trans-unit id="2e75ae3885931555902da6b288ed616843d5dc3c" datatype="html">
<source>Search settings</source> <source>Search settings</source>
<context-group purpose="location"> <context-group purpose="location">
@ -1467,8 +1491,8 @@
</context-group> </context-group>
<target>High</target> <target>High</target>
</trans-unit> </trans-unit>
<trans-unit id="03d4d456b5f14c7bdd428393e17c724cd5210852" datatype="html"> <trans-unit id="04e4b67fdc5f633a81e0ab32c1751ab20e6e3034" datatype="html">
<source> settings saved</source> <source>settings saved</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/_abstract/abstract.settings.component.ts</context> <context context-type="sourcefile">frontend/app/settings/_abstract/abstract.settings.component.ts</context>
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
@ -1591,13 +1615,13 @@
</context-group> </context-group>
<target>Other</target> <target>Other</target>
</trans-unit> </trans-unit>
<trans-unit id="59b428a89fc1e287f2fb60806ed5f0588cb9c270" datatype="html"> <trans-unit id="e636143d733fa6a21fa62ffdff2fd76c221e6cac" datatype="html">
<source>Random Photo</source> <source>Random Media</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/random-photo/random-photo.settings.component.ts</context> <context context-type="sourcefile">frontend/app/settings/random-photo/random-photo.settings.component.ts</context>
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
<target>Random Photo</target> <target>Random Media</target>
</trans-unit> </trans-unit>
<trans-unit id="45dda89cf029b7d7b457a8dff01dc4b9a6485816" datatype="html"> <trans-unit id="45dda89cf029b7d7b457a8dff01dc4b9a6485816" datatype="html">
<source>Thumbnail</source> <source>Thumbnail</source>
@ -1639,6 +1663,14 @@
</context-group> </context-group>
<target>Password protection disabled</target> <target>Password protection disabled</target>
</trans-unit> </trans-unit>
<trans-unit id="2d1ea268a6a9f483dbc2cbfe19bf4256a57a6af4" datatype="html">
<source>Video</source>
<context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/video/video.settings.component.ts</context>
<context context-type="linenumber">1</context>
</context-group>
<target>Video</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@ -104,7 +104,7 @@
<source>download</source> <source>download</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">25</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
<target>letöltés</target> <target>letöltés</target>
</trans-unit> </trans-unit>
@ -112,7 +112,7 @@
<source>info</source> <source>info</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">29</context> <context context-type="linenumber">31</context>
</context-group> </context-group>
<target>info</target> <target>info</target>
</trans-unit> </trans-unit>
@ -120,11 +120,11 @@
<source>toggle fullscreen</source> <source>toggle fullscreen</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">36</context> <context context-type="linenumber">38</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">45</context> <context context-type="linenumber">47</context>
</context-group> </context-group>
<target>teljes képernyőre váltás</target> <target>teljes képernyőre váltás</target>
</trans-unit> </trans-unit>
@ -132,7 +132,7 @@
<source>close</source> <source>close</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">52</context> <context context-type="linenumber">54</context>
</context-group> </context-group>
<target>bezárás</target> <target>bezárás</target>
</trans-unit> </trans-unit>
@ -140,7 +140,7 @@
<source>key: left arrow</source> <source>key: left arrow</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">60</context> <context context-type="linenumber">68</context>
</context-group> </context-group>
<target>billentyű: balra nyíl</target> <target>billentyű: balra nyíl</target>
</trans-unit> </trans-unit>
@ -148,7 +148,7 @@
<source>key: right arrow</source> <source>key: right arrow</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context> <context context-type="sourcefile">app/gallery/lightbox/lightbox.gallery.component.html</context>
<context context-type="linenumber">64</context> <context context-type="linenumber">72</context>
</context-group> </context-group>
<target>billentyű: jobbra nyíl</target> <target>billentyű: jobbra nyíl</target>
</trans-unit> </trans-unit>
@ -607,6 +607,10 @@
<context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context> <context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context>
<context context-type="linenumber">104</context> <context context-type="linenumber">104</context>
</context-group> </context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/settings/video/video.settings.component.html</context>
<context context-type="linenumber">28</context>
</context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/settings/search/search.settings.component.html</context> <context context-type="sourcefile">app/settings/search/search.settings.component.html</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">73</context>
@ -640,6 +644,10 @@
<context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context> <context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context>
<context context-type="linenumber">107</context> <context context-type="linenumber">107</context>
</context-group> </context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/settings/video/video.settings.component.html</context>
<context context-type="linenumber">31</context>
</context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/settings/search/search.settings.component.html</context> <context context-type="sourcefile">app/settings/search/search.settings.component.html</context>
<context context-type="linenumber">76</context> <context context-type="linenumber">76</context>
@ -796,6 +804,22 @@
</context-group> </context-group>
<target>';' elválasztott egész számok. Ha a méretnek pl. 200-t állítasz be, akkor a thumbnail 200^2 pixelből fog állni.</target> <target>';' elválasztott egész számok. Ha a méretnek pl. 200-t állítasz be, akkor a thumbnail 200^2 pixelből fog állni.</target>
</trans-unit> </trans-unit>
<trans-unit id="b5398623f87ee72ed23f5023918db1707771e925" datatype="html">
<source>Video settings</source>
<context-group purpose="location">
<context context-type="sourcefile">app/settings/video/video.settings.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
<target>Videó beállítások</target>
</trans-unit>
<trans-unit id="6b8862cecf0fd1c8fc1b503e131bfa230d851343" datatype="html">
<source>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.</source>
<context-group purpose="location">
<context context-type="sourcefile">app/settings/video/video.settings.component.html</context>
<context context-type="linenumber">23</context>
</context-group>
<target>A videó lejátszához az ffmpeg szükséges. Az ffmpeg és az ffprobe binárisoknak rendelkezésre kell állniuk a PATH-ban vagy az @ffmpeg-installer/ffmpeg és @ffprobe-installer/ffprobe opcionális node csomagokat kell telepíteni.</target>
</trans-unit>
<trans-unit id="2e75ae3885931555902da6b288ed616843d5dc3c" datatype="html"> <trans-unit id="2e75ae3885931555902da6b288ed616843d5dc3c" datatype="html">
<source>Search settings</source> <source>Search settings</source>
<context-group purpose="location"> <context-group purpose="location">
@ -1467,8 +1491,8 @@
</context-group> </context-group>
<target>Magas</target> <target>Magas</target>
</trans-unit> </trans-unit>
<trans-unit id="03d4d456b5f14c7bdd428393e17c724cd5210852" datatype="html"> <trans-unit id="04e4b67fdc5f633a81e0ab32c1751ab20e6e3034" datatype="html">
<source> settings saved</source> <source>settings saved</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/_abstract/abstract.settings.component.ts</context> <context context-type="sourcefile">frontend/app/settings/_abstract/abstract.settings.component.ts</context>
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
@ -1591,13 +1615,13 @@
</context-group> </context-group>
<target>Egyéb</target> <target>Egyéb</target>
</trans-unit> </trans-unit>
<trans-unit id="59b428a89fc1e287f2fb60806ed5f0588cb9c270" datatype="html"> <trans-unit id="e636143d733fa6a21fa62ffdff2fd76c221e6cac" datatype="html">
<source>Random Photo</source> <source>Random Media</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/random-photo/random-photo.settings.component.ts</context> <context context-type="sourcefile">frontend/app/settings/random-photo/random-photo.settings.component.ts</context>
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
<target>Véletlen fotó</target> <target>Véletlenszerű média</target>
</trans-unit> </trans-unit>
<trans-unit id="45dda89cf029b7d7b457a8dff01dc4b9a6485816" datatype="html"> <trans-unit id="45dda89cf029b7d7b457a8dff01dc4b9a6485816" datatype="html">
<source>Thumbnail</source> <source>Thumbnail</source>
@ -1639,6 +1663,14 @@
</context-group> </context-group>
<target>Jelszavas védelem tiltva</target> <target>Jelszavas védelem tiltva</target>
</trans-unit> </trans-unit>
<trans-unit id="2d1ea268a6a9f483dbc2cbfe19bf4256a57a6af4" datatype="html">
<source>Video</source>
<context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/video/video.settings.component.ts</context>
<context context-type="linenumber">1</context>
</context-group>
<target>Videó</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@ -119,9 +119,11 @@
"natives": "1.1.3" "natives": "1.1.3"
}, },
"optionalDependencies": { "optionalDependencies": {
"mysql": "2.16.0", "@ffmpeg-installer/ffmpeg": "1.0.16",
"@ffprobe-installer/ffprobe": "1.0.9",
"bcrypt": "3.0.2", "bcrypt": "3.0.2",
"gm": "1.23.1", "gm": "1.23.1",
"mysql": "2.16.0",
"sharp": "0.21.0" "sharp": "0.21.0"
}, },
"engines": { "engines": {