1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-02-11 13:53:28 +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 {DataBaseConfig, DatabaseType, IndexingConfig, IPrivateConfig, ThumbnailConfig} from '../../common/config/private/IPrivateConfig';
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 {BasicConfigDTO} from '../../common/entities/settings/BasicConfigDTO';
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));
}
}
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) {
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) {
return next();
}
@ -61,17 +61,6 @@ export class GalleryMWs {
if (cw.notModified === true) {
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[]) => {
media.forEach(m => {
@ -89,7 +78,7 @@ export class GalleryMWs {
};
if (cw.directory) {
removeDirs(cw.directory);
DirectoryDTO.removeReferences(cw.directory);
// TODO: remove when typeorm inheritance is fixed
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();
}

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 {
DataBaseConfig,
DatabaseType,
IPrivateConfig,
ThumbnailConfig,
ThumbnailProcessingLib
} from '../../common/config/private/IPrivateConfig';
import {Logger} from '../Logger';
import {NotificationManager} from './NotifocationManager';
import {ProjectPath} from '../ProjectPath';
import {SQLConnection} from './sql/SQLConnection';
} from '../../../common/config/private/IPrivateConfig';
import {Logger} from '../../Logger';
import {NotificationManager} from '../NotifocationManager';
import {ProjectPath} from '../../ProjectPath';
import {SQLConnection} from '../sql/SQLConnection';
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]';
@ -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) {
switch (processingLibrary) {
case ThumbnailProcessingLib.sharp:
@ -44,8 +71,8 @@ export class ConfigDiagnostics {
}
}
static async testThumbnailFolder(folder: string) {
await new Promise((resolve, reject) => {
static testThumbnailFolder(folder: string) {
return new Promise((resolve, reject) => {
fs.access(folder, fs.constants.W_OK, (err) => {
if (err) {
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) {
await new Promise((resolve, reject) => {
static testImageFolder(folder: string) {
return new Promise((resolve, reject) => {
if (!fs.existsSync(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 {
await ConfigDiagnostics.testImageFolder(Config.Server.imagesFolder);
} 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 {
private static VERSION = 1;
private static VERSION = 2;
constructor() {
}

View File

@ -5,15 +5,17 @@ import {CameraMetadata, GPSMetadata, PhotoDTO, PhotoMetadata} from '../../../com
import {Logger} from '../../Logger';
import {IptcParser} from 'ts-node-iptc';
import {ExifParserFactory, OrientationTypes} from 'ts-exif-parser';
import * as ffmpeg from 'fluent-ffmpeg';
import {FfprobeData} from 'fluent-ffmpeg';
import {ProjectPath} from '../../ProjectPath';
import {Config} from '../../../common/config/private/Config';
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 ffmpeg = FFmpegFactory.get();
export class DiskMangerWorker {
private static isImage(fullPath: string) {
const extensions = [
@ -33,8 +35,7 @@ export class DiskMangerWorker {
private static isVideo(fullPath: string) {
const extensions = [
'.mp4',
'.webm'
'.mp4'
];
const extension = path.extname(fullPath).toLowerCase();
@ -83,7 +84,8 @@ export class DiskMangerWorker {
if (maxPhotos != null && directory.media.length > maxPhotos) {
break;
}
} else if (DiskMangerWorker.isVideo(fullFilePath)) {
} else if (Config.Client.Video.enabled === true &&
DiskMangerWorker.isVideo(fullFilePath)) {
directory.media.push(<VideoDTO>{
name: file,
directory: null,
@ -98,7 +100,7 @@ export class DiskMangerWorker {
return resolve(directory);
} 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 {FfmpegCommand, FfprobeData} from 'fluent-ffmpeg';
import {ThumbnailProcessingLib} from '../../../common/config/private/IPrivateConfig';
import {FFmpegFactory} from '../FFmpegFactory';
export class ThumbnailWorker {
@ -50,7 +51,7 @@ export interface RendererInput {
export class VideoRendererFactory {
public static build(): (input: RendererInput) => Promise<void> {
const ffmpeg = require('fluent-ffmpeg');
const ffmpeg = FFmpegFactory.get();
return (input: RendererInput): Promise<void> => {
return new Promise((resolve, reject) => {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
import {MediaDTO} from './MediaDTO';
import {PhotoDTO} from './PhotoDTO';
export interface DirectoryDTO {
id: number;
@ -23,4 +24,16 @@ export module DirectoryDTO {
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-thumbnail #thumbnail [hidden]="!thumbnail.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
<app-settings-video #video [hidden]="!video.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-video>
<app-settings-search #search [hidden]="!search.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-search>
<app-settings-share #share [hidden]="!share.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-share>
<app-settings-map #map [hidden]="!map.hasAvailableSettings" [simplifiedMode]="simplifiedMode"></app-settings-map>
<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"
[simplifiedMode]="simplifiedMode"></app-settings-other>
<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 {RandomPhotoSettingsComponent} from './settings/random-photo/random-photo.settings.component';
import {FixOrientationPipe} from './gallery/FixOrientationPipe';
import {VideoSettingsComponent} from './settings/video/video.settings.component';
@Injectable()
export class GoogleMapsConfig {
@ -159,6 +160,7 @@ export function translationsFactory(locale: string) {
DatabaseSettingsComponent,
MapSettingsComponent,
ThumbnailSettingsComponent,
VideoSettingsComponent,
SearchSettingsComponent,
ShareSettingsComponent,
RandomPhotoSettingsComponent,

View File

@ -135,7 +135,7 @@ export abstract class SettingsComponent<T, S extends AbstractSettingsService<T>
try {
await this._settingsService.updateSettings(this.settings);
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;
return true;
} catch (err) {

View File

@ -16,9 +16,9 @@
<ng-container *ngIf="settings.type == DatabaseType.mysql">
<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>
<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>
<input type="text" class="form-control" i18n-placeholder placeholder="Username"
[(ngModel)]="settings.mysql.username" name="username">

View File

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

View File

@ -74,7 +74,7 @@
</div>
<form #NewUserForm="ngForm">
<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>
<input type="password" class="form-control" i18n-placeholder placeholder="Password"
[(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>
<context-group purpose="location">
<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>
<target>download</target>
</trans-unit>
@ -112,7 +112,7 @@
<source>info</source>
<context-group purpose="location">
<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>
<target>info</target>
</trans-unit>
@ -120,11 +120,11 @@
<source>toggle fullscreen</source>
<context-group purpose="location">
<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 purpose="location">
<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>
<target>toggle fullscreen</target>
</trans-unit>
@ -132,7 +132,7 @@
<source>close</source>
<context-group purpose="location">
<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>
<target>close</target>
</trans-unit>
@ -140,7 +140,7 @@
<source>key: left arrow</source>
<context-group purpose="location">
<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>
<target>key: left arrow</target>
</trans-unit>
@ -148,7 +148,7 @@
<source>key: right arrow</source>
<context-group purpose="location">
<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>
<target>key: right arrow</target>
</trans-unit>
@ -607,6 +607,10 @@
<context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context>
<context context-type="linenumber">104</context>
</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 context-type="sourcefile">app/settings/search/search.settings.component.html</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="linenumber">107</context>
</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 context-type="sourcefile">app/settings/search/search.settings.component.html</context>
<context context-type="linenumber">76</context>
@ -796,6 +804,22 @@
</context-group>
<target>';' separated integers. If size is 200, that thumbnail will have 200^2 pixels.</target>
</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">
<source>Search settings</source>
<context-group purpose="location">
@ -1467,8 +1491,8 @@
</context-group>
<target>High</target>
</trans-unit>
<trans-unit id="03d4d456b5f14c7bdd428393e17c724cd5210852" datatype="html">
<source> settings saved</source>
<trans-unit id="04e4b67fdc5f633a81e0ab32c1751ab20e6e3034" datatype="html">
<source>settings saved</source>
<context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/_abstract/abstract.settings.component.ts</context>
<context context-type="linenumber">1</context>
@ -1591,13 +1615,13 @@
</context-group>
<target>Other</target>
</trans-unit>
<trans-unit id="59b428a89fc1e287f2fb60806ed5f0588cb9c270" datatype="html">
<source>Random Photo</source>
<trans-unit id="e636143d733fa6a21fa62ffdff2fd76c221e6cac" datatype="html">
<source>Random Media</source>
<context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/random-photo/random-photo.settings.component.ts</context>
<context context-type="linenumber">1</context>
</context-group>
<target>Random Photo</target>
<target>Random Media</target>
</trans-unit>
<trans-unit id="45dda89cf029b7d7b457a8dff01dc4b9a6485816" datatype="html">
<source>Thumbnail</source>
@ -1639,6 +1663,14 @@
</context-group>
<target>Password protection disabled</target>
</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>
</file>
</xliff>

View File

@ -104,7 +104,7 @@
<source>download</source>
<context-group purpose="location">
<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>
<target>letöltés</target>
</trans-unit>
@ -112,7 +112,7 @@
<source>info</source>
<context-group purpose="location">
<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>
<target>info</target>
</trans-unit>
@ -120,11 +120,11 @@
<source>toggle fullscreen</source>
<context-group purpose="location">
<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 purpose="location">
<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>
<target>teljes képernyőre váltás</target>
</trans-unit>
@ -132,7 +132,7 @@
<source>close</source>
<context-group purpose="location">
<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>
<target>bezárás</target>
</trans-unit>
@ -140,7 +140,7 @@
<source>key: left arrow</source>
<context-group purpose="location">
<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>
<target>billentyű: balra nyíl</target>
</trans-unit>
@ -148,7 +148,7 @@
<source>key: right arrow</source>
<context-group purpose="location">
<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>
<target>billentyű: jobbra nyíl</target>
</trans-unit>
@ -607,6 +607,10 @@
<context context-type="sourcefile">app/settings/thumbnail/thumbanil.settings.component.html</context>
<context context-type="linenumber">104</context>
</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 context-type="sourcefile">app/settings/search/search.settings.component.html</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="linenumber">107</context>
</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 context-type="sourcefile">app/settings/search/search.settings.component.html</context>
<context context-type="linenumber">76</context>
@ -796,6 +804,22 @@
</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>
</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">
<source>Search settings</source>
<context-group purpose="location">
@ -1467,8 +1491,8 @@
</context-group>
<target>Magas</target>
</trans-unit>
<trans-unit id="03d4d456b5f14c7bdd428393e17c724cd5210852" datatype="html">
<source> settings saved</source>
<trans-unit id="04e4b67fdc5f633a81e0ab32c1751ab20e6e3034" datatype="html">
<source>settings saved</source>
<context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/_abstract/abstract.settings.component.ts</context>
<context context-type="linenumber">1</context>
@ -1591,13 +1615,13 @@
</context-group>
<target>Egyéb</target>
</trans-unit>
<trans-unit id="59b428a89fc1e287f2fb60806ed5f0588cb9c270" datatype="html">
<source>Random Photo</source>
<trans-unit id="e636143d733fa6a21fa62ffdff2fd76c221e6cac" datatype="html">
<source>Random Media</source>
<context-group purpose="location">
<context context-type="sourcefile">frontend/app/settings/random-photo/random-photo.settings.component.ts</context>
<context context-type="linenumber">1</context>
</context-group>
<target>Véletlen fotó</target>
<target>Véletlenszerű média</target>
</trans-unit>
<trans-unit id="45dda89cf029b7d7b457a8dff01dc4b9a6485816" datatype="html">
<source>Thumbnail</source>
@ -1639,6 +1663,14 @@
</context-group>
<target>Jelszavas védelem tiltva</target>
</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>
</file>
</xliff>

View File

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