refactor(server): modularize getFfmpegOptions (#3138)

* refactored `getFfmpegOptions`

refactor transcoding, make separate service

* fixed enum casing

* use `Logger` instead of `console.log`

* review suggestions

* use enum for `getHandler`

* fixed formatting

* Update server/src/domain/media/media.util.ts

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* Update server/src/domain/media/media.util.ts

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* More specific imports, renamed codec classes

* simplified code

* removed unused import

* added tests

* added base implementation for bitrate and threads

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
Mert
2023-07-08 22:43:11 -04:00
committed by GitHub
parent 27018e4ab6
commit 8349a28ed8
33 changed files with 1131 additions and 345 deletions

View File

@@ -51,24 +51,36 @@ export enum SystemConfigKey {
STORAGE_TEMPLATE = 'storageTemplate.template',
}
export enum TranscodePreset {
export enum TranscodePolicy {
ALL = 'all',
OPTIMAL = 'optimal',
REQUIRED = 'required',
DISABLED = 'disabled',
}
export enum VideoCodec {
H264 = 'h264',
HEVC = 'hevc',
VP9 = 'vp9',
}
export enum AudioCodec {
MP3 = 'mp3',
AAC = 'aac',
OPUS = 'opus',
}
export interface SystemConfig {
ffmpeg: {
crf: number;
threads: number;
preset: string;
targetVideoCodec: string;
targetAudioCodec: string;
targetVideoCodec: VideoCodec;
targetAudioCodec: AudioCodec;
targetResolution: string;
maxBitrate: string;
twoPass: boolean;
transcode: TranscodePreset;
transcode: TranscodePolicy;
};
job: Record<QueueName, { concurrency: number }>;
oauth: {

View File

@@ -65,7 +65,6 @@ const providers: Provider[] = [
{ provide: IJobRepository, useClass: JobRepository },
{ provide: IKeyRepository, useClass: APIKeyRepository },
{ provide: IMachineLearningRepository, useClass: MachineLearningRepository },
{ provide: IMediaRepository, useClass: MediaRepository },
{ provide: IPartnerRepository, useClass: PartnerRepository },
{ provide: IPersonRepository, useClass: PersonRepository },
{ provide: ISearchRepository, useClass: TypesenseRepository },
@@ -74,6 +73,7 @@ const providers: Provider[] = [
{ provide: IStorageRepository, useClass: FilesystemProvider },
{ provide: ISystemConfigRepository, useClass: SystemConfigRepository },
{ provide: ITagRepository, useClass: TagRepository },
{ provide: IMediaRepository, useClass: MediaRepository },
{ provide: IUserRepository, useClass: UserRepository },
{ provide: IUserTokenRepository, useClass: UserTokenRepository },
];

View File

@@ -1,4 +1,5 @@
import { CropOptions, IMediaRepository, ResizeOptions, TranscodeOptions, VideoInfo } from '@app/domain';
import { Logger } from '@nestjs/common';
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg';
import fs from 'fs/promises';
import sharp from 'sharp';
@@ -7,6 +8,8 @@ import { promisify } from 'util';
const probe = promisify<string, FfprobeData>(ffmpeg.ffprobe);
export class MediaRepository implements IMediaRepository {
private logger = new Logger(MediaRepository.name);
crop(input: string, options: CropOptions): Promise<Buffer> {
return sharp(input, { failOnError: false })
.extract({
@@ -47,7 +50,10 @@ export class MediaRepository implements IMediaRepository {
`-vf scale='min(${size},iw)':'min(${size},ih)':force_original_aspect_ratio=increase`,
])
.output(output)
.on('error', reject)
.on('error', (err, stdout, stderr) => {
this.logger.error(stderr);
reject(err);
})
.on('end', resolve)
.run();
});
@@ -87,7 +93,10 @@ export class MediaRepository implements IMediaRepository {
ffmpeg(input, { niceness: 10 })
.outputOptions(options.outputOptions)
.output(output)
.on('error', reject)
.on('error', (err, stdout, stderr) => {
this.logger.error(stderr);
reject(err);
})
.on('end', resolve)
.run();
});
@@ -102,7 +111,10 @@ export class MediaRepository implements IMediaRepository {
.addOptions('-passlogfile', output)
.addOptions('-f null')
.output('/dev/null') // first pass output is not saved as only the .log file is needed
.on('error', reject)
.on('error', (err, stdout, stderr) => {
this.logger.error(stderr);
reject(err);
})
.on('end', () => {
// second pass
ffmpeg(input, { niceness: 10 })
@@ -110,7 +122,10 @@ export class MediaRepository implements IMediaRepository {
.addOptions('-pass', '2')
.addOptions('-passlogfile', output)
.output(output)
.on('error', reject)
.on('error', (err, stdout, stderr) => {
this.logger.error(stderr);
reject(err);
})
.on('end', () => fs.unlink(`${output}-0.log`))
.on('end', () => fs.rm(`${output}-0.log.mbtree`, { force: true }))
.on('end', resolve)