feat(server): transcoding improvements (#1370)

* feat: support isEdited flag for SettingSwitch

* feat: add transcodeAll ffmpeg settings for extra transcoding control

* refactor: tidy up and rename current video transcoding code + transcode everything

* feat: better video transcoding with ffprobe

analyses video files to see if they are already in the desired format
allows admin to choose to transcode all videos regardless of the current format

* fix: always serve encoded video if it exists

* feat: change video codec option to a select box, limit options

removed previous video codec config option as it's incompatible with new options
removed mapping for encoder to codec as we now store the codec in the config

* feat: add video conversion job for transcoding previously missed videos

* chore: fix spelling of job messages to pluralise assets

* chore: fix prettier/eslint warnings

* feat: force switch targetAudioCodec default to aac to avoid iOS incompatibility

* chore: lint issues after rebase
This commit is contained in:
Zack Pollard 2023-01-22 02:09:02 +00:00 committed by GitHub
parent 8eb82836b9
commit 4e0fe27de3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 274 additions and 63 deletions

View file

@ -1,10 +1,10 @@
import { AssetEntity } from '@app/infra/db/entities';
export interface IMp4ConversionProcessor {
export interface IVideoConversionProcessor {
/**
* The Asset entity that was saved in the database
*/
asset: AssetEntity;
}
export type IVideoTranscodeJob = IMp4ConversionProcessor;
export type IVideoTranscodeJob = IVideoConversionProcessor;

View file

@ -12,7 +12,7 @@ export enum QueueName {
export enum JobName {
ASSET_UPLOADED = 'asset-uploaded',
MP4_CONVERSION = 'mp4-conversion',
VIDEO_CONVERSION = 'mp4-conversion',
GENERATE_JPEG_THUMBNAIL = 'generate-jpeg-thumbnail',
GENERATE_WEBP_THUMBNAIL = 'generate-webp-thumbnail',
EXIF_EXTRACTION = 'exif-extraction',

View file

@ -3,7 +3,7 @@ import {
IDeleteFileOnDiskJob,
IExifExtractionProcessor,
IMachineLearningJob,
IMp4ConversionProcessor,
IVideoConversionProcessor,
IReverseGeocodingProcessor,
IUserDeletionJob,
JpegGeneratorProcessor,
@ -13,7 +13,7 @@ import { JobName } from './job.constants';
export type JobItem =
| { name: JobName.ASSET_UPLOADED; data: IAssetUploadedJob }
| { name: JobName.MP4_CONVERSION; data: IMp4ConversionProcessor }
| { name: JobName.VIDEO_CONVERSION; data: IVideoConversionProcessor }
| { name: JobName.GENERATE_JPEG_THUMBNAIL; data: JpegGeneratorProcessor }
| { name: JobName.GENERATE_WEBP_THUMBNAIL; data: WebpGeneratorProcessor }
| { name: JobName.EXIF_EXTRACTION; data: IExifExtractionProcessor }

View file

@ -1,4 +1,4 @@
import { IsString } from 'class-validator';
import { IsBoolean, IsString } from 'class-validator';
export class SystemConfigFFmpegDto {
@IsString()
@ -15,4 +15,7 @@ export class SystemConfigFFmpegDto {
@IsString()
targetScaling!: string;
@IsBoolean()
transcodeAll!: boolean;
}

View file

@ -11,9 +11,10 @@ const defaults: SystemConfig = Object.freeze({
ffmpeg: {
crf: '23',
preset: 'ultrafast',
targetVideoCodec: 'libx264',
targetAudioCodec: 'mp3',
targetVideoCodec: 'h264',
targetAudioCodec: 'aac',
targetScaling: '1280:-2',
transcodeAll: false,
},
oauth: {
enabled: false,

View file

@ -15,9 +15,10 @@ const updatedConfig = Object.freeze({
ffmpeg: {
crf: 'a new value',
preset: 'ultrafast',
targetAudioCodec: 'mp3',
targetAudioCodec: 'aac',
targetScaling: '1280:-2',
targetVideoCodec: 'libx264',
targetVideoCodec: 'h264',
transcodeAll: false,
},
oauth: {
autoLaunch: true,

View file

@ -48,9 +48,10 @@ export const systemConfigStub = {
ffmpeg: {
crf: '23',
preset: 'ultrafast',
targetAudioCodec: 'mp3',
targetAudioCodec: 'aac',
targetScaling: '1280:-2',
targetVideoCodec: 'libx264',
targetVideoCodec: 'h264',
transcodeAll: false,
},
oauth: {
autoLaunch: false,

View file

@ -18,6 +18,7 @@ export enum SystemConfigKey {
FFMPEG_TARGET_VIDEO_CODEC = 'ffmpeg.targetVideoCodec',
FFMPEG_TARGET_AUDIO_CODEC = 'ffmpeg.targetAudioCodec',
FFMPEG_TARGET_SCALING = 'ffmpeg.targetScaling',
FFMPEG_TRANSCODE_ALL = 'ffmpeg.transcodeAll',
OAUTH_ENABLED = 'oauth.enabled',
OAUTH_ISSUER_URL = 'oauth.issuerUrl',
OAUTH_CLIENT_ID = 'oauth.clientId',
@ -39,6 +40,7 @@ export interface SystemConfig {
targetVideoCodec: string;
targetAudioCodec: string;
targetScaling: string;
transcodeAll: boolean;
};
oauth: {
enabled: boolean;

View file

@ -0,0 +1,12 @@
import {MigrationInterface, QueryRunner} from 'typeorm';
export class RemoveVideoCodecConfigOption1674263302006 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetVideoCodec'`);
await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetAudioCodec'`);
}
public async down(): Promise<void> {
// noop
}
}