Update flows

This commit is contained in:
HaveAGitGat 2023-08-26 17:53:07 +01:00
parent 658857fdf4
commit 25c4fab8d9
73 changed files with 4295 additions and 839 deletions

View file

@ -10,7 +10,6 @@ const details = () :IpluginDetails => ({
description: 'Set 10 Bit Video',
style: {
borderColor: '#6efefc',
opacity: 0.5,
},
tags: 'video',
isStartPlugin: false,
@ -31,6 +30,13 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
args.inputs = lib.loadDefaultValues(args.inputs, details);
for (let i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) {
const stream = args.variables.ffmpegCommand.streams[i];
if (stream.codec_type === 'video') {
stream.outputArgs.push('-pix_fmt:v:{outputTypeIndex}', 'p010le', '-profile:v:{outputTypeIndex}', 'main10');
}
}
return {
outputFileObj: args.inputFileObj,
outputNumber: 1,

View file

@ -1,9 +1,11 @@
import {
IffmpegCommandStream,
IpluginDetails,
IpluginInputArgs,
IpluginOutputArgs,
} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces';
import { CLI } from '../../../../FlowHelpers/1.0.0/utils';
import { CLI } from '../../../../FlowHelpers/1.0.0/cliUtils';
import { getContainer } from '../../../../FlowHelpers/1.0.0/fileUtils';
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
const details = (): IpluginDetails => ({
@ -21,24 +23,41 @@ const details = (): IpluginDetails => ({
outputs: [
{
number: 1,
tooltip: 'File is 480p',
},
{
number: 2,
tooltip: 'File is 576p',
tooltip: 'Continue to next plugin',
},
],
});
const getEncoder = (codec: string) => {
switch (codec) {
case 'h264':
return 'libx264';
case 'hevc':
return 'libx265';
default:
return codec;
const getOuputStreamIndex = (streams: IffmpegCommandStream[], stream: IffmpegCommandStream): number => {
let index = -1;
for (let idx = 0; idx < streams.length; idx += 1) {
if (!stream.removed) {
index += 1;
}
if (streams[idx].index === stream.index) {
break;
}
}
return index;
};
const getOuputStreamTypeIndex = (streams: IffmpegCommandStream[], stream: IffmpegCommandStream): number => {
let index = -1;
for (let idx = 0; idx < streams.length; idx += 1) {
if (!stream.removed && streams[idx].codec_type === stream.codec_type) {
index += 1;
}
if (streams[idx].index === stream.index) {
break;
}
}
return index;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -53,25 +72,48 @@ const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
cliArgs.push('-i');
cliArgs.push(args.inputFileObj._id);
let shouldProcess = false;
const inputArgs: string[] = [];
let { shouldProcess, streams } = args.variables.ffmpegCommand;
// @ts-expect-error type
args.variables.ffmpegCommand.streams.forEach((stream) => {
if (!stream.removed) {
cliArgs.push('-map');
cliArgs.push(`0:${stream.index}`);
cliArgs.push(`-c:${stream.index}`);
args.jobLog(JSON.stringify({ stream }));
if (args.inputs.forceProcess || stream.codec_name !== stream.targetCodec) {
shouldProcess = true;
cliArgs.push(getEncoder(stream.targetCodec));
} else {
cliArgs.push('copy');
}
streams = streams.filter((stream) => {
if (stream.removed) {
shouldProcess = true;
}
return !stream.removed;
});
if (getContainer(args.inputFileObj._id) !== args.variables.ffmpegCommand.container) {
shouldProcess = true;
}
for (let i = 0; i < streams.length; i += 1) {
const stream = streams[i];
stream.outputArgs = stream.outputArgs.map((arg) => {
if (arg.includes('{outputIndex}')) {
// eslint-disable-next-line no-param-reassign
arg = arg.replace('{outputIndex}', String(getOuputStreamIndex(streams, stream)));
}
if (arg.includes('{outputTypeIndex}')) {
// eslint-disable-next-line no-param-reassign
arg = arg.replace('{outputTypeIndex}', String(getOuputStreamTypeIndex(streams, stream)));
}
return arg;
});
cliArgs.push(...stream.mapArgs);
if (stream.outputArgs.length === 0) {
cliArgs.push(`-c:${getOuputStreamIndex(streams, stream)}`, 'copy');
} else {
cliArgs.push(...stream.outputArgs);
}
inputArgs.push(...stream.inputArgs);
}
if (!shouldProcess) {
args.jobLog('No need to process file, already as required');
return {
@ -81,12 +123,11 @@ const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
};
}
// @ts-expect-error type
const outputFilePath = `${args.workDir}/tempFile.${args.variables.ffmpegCommand.container}`;
cliArgs.push(outputFilePath);
const idx = cliArgs.indexOf('-i');
cliArgs.splice(idx, 0, ...inputArgs);
// @ts-expect-error type
args.deps.fsextra.ensureDirSync(args.workDir);
const outputFilePath = `${args.workDir}/tempFile_${new Date().getTime()}.${args.variables.ffmpegCommand.container}`;
cliArgs.push(outputFilePath);
args.jobLog('Processing file');
args.jobLog(JSON.stringify({
@ -94,6 +135,11 @@ const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
outputFilePath,
}));
args.updateWorker({
CLIType: args.ffmpegPath,
preset: cliArgs.join(' '),
});
const cli = new CLI({
cli: args.ffmpegPath,
spawnArgs: cliArgs,
@ -107,10 +153,6 @@ const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
const res = await cli.runCli();
if (!args.logFullCliOutput) {
args.jobLog(res.errorLogFull.slice(-1000).join(''));
}
if (res.cliExitCode !== 0) {
args.jobLog('Running FFmpeg failed');
throw new Error('FFmpeg failed');

View file

@ -33,7 +33,6 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
args.inputs = lib.loadDefaultValues(args.inputs, details);
// @ts-expect-error type
args.variables.ffmpegCommand.streams.forEach((stream) => {
if (stream.codec_type === 'data') {
stream.removed = true;

View file

@ -32,7 +32,6 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
args.inputs = lib.loadDefaultValues(args.inputs, details);
// @ts-expect-error type
args.variables.ffmpegCommand.streams.forEach((stream) => {
if (stream.codec_type === 'subtitle') {
stream.removed = true;

View file

@ -1,22 +1,93 @@
import {
IffmpegCommandStream,
IpluginDetails,
IpluginInputArgs,
IpluginOutputArgs,
} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces';
import { Istreams } from '../../../../FlowHelpers/1.0.0/interfaces/synced/IFileObject';
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
const details = () :IpluginDetails => ({
name: 'Set Video Bitrate',
description: 'Set Video Bitrate',
const details = (): IpluginDetails => ({
name: 'Reorder Streams',
description: 'Reorder Streams',
style: {
borderColor: '#6efefc',
opacity: 0.5,
},
tags: 'video',
isStartPlugin: false,
sidebarPosition: -1,
icon: '',
inputs: [],
inputs: [
{
name: 'processOrder',
type: 'string',
defaultValue: 'codecs,channels,languages,streamTypes',
inputUI: {
type: 'text',
},
tooltip:
`Specify the process order.
For example, if 'languages' is first, the streams will be ordered based on that first.
So put the most important properties last.
The default order is suitable for most people.
\\nExample:\\n
codecs,channels,languages,streamTypes
`,
},
{
name: 'languages',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip:
`Specify the language tags order, separated by commas. Leave blank to disable.
\\nExample:\\n
eng,fre
`,
},
{
name: 'channels',
type: 'string',
defaultValue: '7.1,5.1,2,1',
inputUI: {
type: 'text',
},
tooltip:
`Specify the channels order, separated by commas. Leave blank to disable.
\\nExample:\\n
7.1,5.1,2,1`,
},
{
name: 'codecs',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip:
`Specify the codec order, separated by commas. Leave blank to disable.
\\nExample:\\n
aac,ac3`,
},
{
name: 'streamTypes',
type: 'string',
defaultValue: 'video,audio,subtitle',
inputUI: {
type: 'text',
},
tooltip:
`Specify the streamTypes order, separated by commas. Leave blank to disable.
\\nExample:\\n
video,audio,subtitle
`,
},
],
outputs: [
{
number: 1,
@ -26,11 +97,125 @@ const details = () :IpluginDetails => ({
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
const plugin = (args: IpluginInputArgs): IpluginOutputArgs => {
const lib = require('../../../../../methods/lib')();
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
args.inputs = lib.loadDefaultValues(args.inputs, details);
let streams: IffmpegCommandStream[] = JSON.parse(JSON.stringify(args.variables.ffmpegCommand.streams));
const originalStreams = JSON.stringify(streams);
streams.forEach((stream, index) => {
// eslint-disable-next-line no-param-reassign
stream.typeIndex = index;
});
const sortStreams = (sortType: {
inputs: string,
getValue: (stream: Istreams) => string,
}) => {
const items = sortType.inputs.split(',');
items.reverse();
for (let i = 0; i < items.length; i += 1) {
const matchedStreams = [];
for (let j = 0; j < streams.length; j += 1) {
if (String(sortType.getValue(streams[j])) === String(items[i])) {
if (
streams[j].codec_long_name
&& (
streams[j].codec_long_name.includes('image')
|| streams[j].codec_name.includes('png')
)
) {
// do nothing, ffmpeg bug, doesn't move image streams
} else {
matchedStreams.push(streams[j]);
streams.splice(j, 1);
j -= 1;
}
}
}
streams = matchedStreams.concat(streams);
}
};
const processOrder = String(args.inputs.processOrder);
const {
languages, codecs, channels, streamTypes,
} = args.inputs;
const sortTypes:{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any,
} = {
languages: {
getValue: (stream: Istreams) => {
if (stream?.tags?.language) {
return stream.tags.language;
}
return '';
},
inputs: languages,
},
codecs: {
getValue: (stream: Istreams) => {
try {
return stream.codec_name;
} catch (err) {
// err
}
return '';
},
inputs: codecs,
},
channels: {
getValue: (stream: Istreams) => {
const chanMap:{
[key: number]: string
} = {
8: '7.1',
6: '5.1',
2: '2',
1: '1',
};
if (stream?.channels && chanMap[stream.channels]) {
return chanMap[stream.channels];
}
return '';
},
inputs: channels,
},
streamTypes: {
getValue: (stream:Istreams) => {
if (stream.codec_type) {
return stream.codec_type;
}
return '';
},
inputs: streamTypes,
},
};
const processOrderArr = processOrder.split(',');
for (let k = 0; k < processOrderArr.length; k += 1) {
if (sortTypes[processOrderArr[k]] && sortTypes[processOrderArr[k]].inputs) {
sortStreams(sortTypes[processOrderArr[k]]);
}
}
if (JSON.stringify(streams) !== originalStreams) {
// eslint-disable-next-line no-param-reassign
args.variables.ffmpegCommand.shouldProcess = true;
// eslint-disable-next-line no-param-reassign
args.variables.ffmpegCommand.streams = streams;
}
return {
outputFileObj: args.inputFileObj,
outputNumber: 1,

View file

@ -46,8 +46,7 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
args.inputs = lib.loadDefaultValues(args.inputs, details);
// @ts-expect-error type
args.variables.ffmpegCommand.container = args.inputs.container;
args.variables.ffmpegCommand.container = String(args.inputs.container);
return {
outputFileObj: args.inputFileObj,

View file

@ -0,0 +1,103 @@
import {
IpluginDetails,
IpluginInputArgs,
IpluginOutputArgs,
} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces';
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
const details = () :IpluginDetails => ({
name: 'Set Video Resolution',
description: 'Change video resolution',
style: {
borderColor: '#6efefc',
},
tags: 'video',
isStartPlugin: false,
sidebarPosition: -1,
icon: '',
inputs: [
{
name: 'targetResolution',
type: 'string',
defaultValue: '1080p',
inputUI: {
type: 'dropdown',
options: [
'480p',
'720p',
'1080p',
'1440p',
'4KUHD',
],
},
tooltip: 'Specify the codec to use',
},
],
outputs: [
{
number: 1,
tooltip: 'Continue to next plugin',
},
],
});
const getVfScale = (
targetResolution: string,
):string[] => {
switch (targetResolution) {
case '480p':
return ['-vf', 'scale=720:-2'];
case '576p':
return ['-vf', 'scale=720:-2'];
case '720p':
return ['-vf', 'scale=1280:-2'];
case '1080p':
return ['-vf', 'scale=1920:-2'];
case '1440p':
return ['-vf', 'scale=2560:-2'];
case '4KUHD':
return ['-vf', 'scale=3840:-2'];
default:
return ['-vf', 'scale=1920:-2'];
}
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
const lib = require('../../../../../methods/lib')();
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
args.inputs = lib.loadDefaultValues(args.inputs, details);
for (let i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) {
const stream = args.variables.ffmpegCommand.streams[i];
if (stream.codec_type === 'video') {
const targetResolution = String(args.inputs.targetResolution);
if (
targetResolution !== args.inputFileObj.video_resolution
) {
// eslint-disable-next-line no-param-reassign
args.variables.ffmpegCommand.shouldProcess = true;
const scaleArgs = getVfScale(targetResolution);
stream.outputArgs.push(...scaleArgs);
}
}
}
return {
outputFileObj: args.inputFileObj,
outputNumber: 1,
variables: args.variables,
};
};
export {
details,
plugin,
};

View file

@ -3,20 +3,30 @@ import {
IpluginInputArgs,
IpluginOutputArgs,
} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces';
import { getFfType } from '../../../../FlowHelpers/1.0.0/fileUtils';
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
const details = () :IpluginDetails => ({
name: 'Reorder Streams',
description: 'Reorder Streams',
name: 'Set Video Bitrate',
description: 'Set Video Bitrate',
style: {
borderColor: '#6efefc',
opacity: 0.5,
},
tags: 'video',
isStartPlugin: false,
sidebarPosition: -1,
icon: '',
inputs: [],
inputs: [
{
name: 'bitrate',
type: 'string',
defaultValue: '5000',
inputUI: {
type: 'text',
},
tooltip: 'Specify bitrate in kbps',
},
],
outputs: [
{
number: 1,
@ -31,6 +41,13 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
args.inputs = lib.loadDefaultValues(args.inputs, details);
args.variables.ffmpegCommand.streams.forEach((stream) => {
if (stream.codec_type === 'video') {
const ffType = getFfType(stream.codec_type);
stream.outputArgs.push(`-b:${ffType}:{outputTypeIndex}`, `${String(args.inputs.bitrate)}k`);
}
});
return {
outputFileObj: args.inputFileObj,
outputNumber: 1,

View file

@ -1,5 +1,6 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
import { getEncoder } from '../../../../FlowHelpers/1.0.0/hardwareUtils';
import {
IpluginDetails,
IpluginInputArgs,
@ -7,7 +8,7 @@ import {
} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces';
/* eslint-disable no-param-reassign */
const details = () :IpluginDetails => ({
const details = (): IpluginDetails => ({
name: 'Set Video Encoder',
description: 'Set the video encoder for all streams',
style: {
@ -19,7 +20,7 @@ const details = () :IpluginDetails => ({
icon: '',
inputs: [
{
name: 'targetCodec',
name: 'outputCodec',
type: 'string',
defaultValue: 'hevc',
inputUI: {
@ -33,6 +34,61 @@ const details = () :IpluginDetails => ({
},
tooltip: 'Specify the codec to use',
},
{
name: 'ffmpegPreset',
type: 'string',
defaultValue: 'fast',
inputUI: {
type: 'dropdown',
options: [
'veryslow',
'slower',
'slow',
'medium',
'fast',
'faster',
'veryfast',
'superfast',
'ultrafast',
],
},
tooltip: 'Specify the codec to use',
},
{
name: 'ffmpegQuality',
type: 'number',
defaultValue: '25',
inputUI: {
type: 'text',
},
tooltip: 'Specify the codec to use',
},
{
name: 'hardwareEncoding',
type: 'boolean',
defaultValue: 'true',
inputUI: {
type: 'dropdown',
options: [
'false',
'true',
],
},
tooltip: 'Specify whether to use hardware encoding if available',
},
{
name: 'hardwareDecoding',
type: 'boolean',
defaultValue: 'true',
inputUI: {
type: 'dropdown',
options: [
'false',
'true',
],
},
tooltip: 'Specify whether to use hardware decoding if available',
},
{
name: 'forceEncoding',
type: 'boolean',
@ -56,19 +112,59 @@ const details = () :IpluginDetails => ({
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
const lib = require('../../../../../methods/lib')();
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
args.inputs = lib.loadDefaultValues(args.inputs, details);
// @ts-expect-error type
args.variables.ffmpegCommand.streams.forEach((stream) => {
const hardwareDecoding = args.inputs.hardwareDecoding === true;
args.variables.ffmpegCommand.hardwareDecoding = hardwareDecoding;
for (let i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) {
const stream = args.variables.ffmpegCommand.streams[i];
if (stream.codec_type === 'video') {
// @ts-expect-error type
stream.targetCodec = args.inputs.targetCodec;
stream.forceEncoding = args.inputs.forceEncoding;
const targetCodec = String(args.inputs.outputCodec);
const ffmpegPreset = String(args.inputs.ffmpegPreset);
const ffmpegQuality = String(args.inputs.ffmpegQuality);
const forceEncoding = args.inputs.forceEncoding === true;
const hardwarEncoding = args.inputs.hardwareEncoding === true;
if (
forceEncoding
|| stream.codec_name !== targetCodec
) {
args.variables.ffmpegCommand.shouldProcess = true;
// eslint-disable-next-line no-await-in-loop
const encoderProperties = await getEncoder({
targetCodec,
hardwareEncoding: hardwarEncoding,
args,
});
stream.outputArgs.push('-c:{outputIndex}', encoderProperties.encoder);
if (encoderProperties.isGpu) {
stream.outputArgs.push('-qp', ffmpegQuality);
} else {
stream.outputArgs.push('-crf', ffmpegQuality);
}
if (ffmpegPreset) {
stream.outputArgs.push('-preset', ffmpegPreset);
}
if (hardwareDecoding) {
stream.inputArgs.push(...encoderProperties.inputArgs);
}
if (encoderProperties.outputArgs) {
stream.outputArgs.push(...encoderProperties.outputArgs);
}
}
}
});
}
return {
outputFileObj: args.inputFileObj,

View file

@ -1,43 +0,0 @@
import {
IpluginDetails,
IpluginInputArgs,
IpluginOutputArgs,
} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces';
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
const details = () :IpluginDetails => ({
name: 'Set Video Scale',
description: 'Change video scale',
style: {
borderColor: '#6efefc',
opacity: 0.5,
},
tags: 'video',
isStartPlugin: false,
sidebarPosition: -1,
icon: '',
inputs: [],
outputs: [
{
number: 1,
tooltip: 'Continue to next plugin',
},
],
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
const lib = require('../../../../../methods/lib')();
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
args.inputs = lib.loadDefaultValues(args.inputs, details);
return {
outputFileObj: args.inputFileObj,
outputNumber: 1,
variables: args.variables,
};
};
export {
details,
plugin,
};

View file

@ -42,10 +42,18 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
streams: JSON.parse(JSON.stringify(args.inputFileObj.ffProbeData.streams)).map((stream:Istreams) => ({
...stream,
removed: false,
targetCodec: stream.codec_name,
args: [],
mapArgs: [
'-map',
`0:${stream.index}`,
],
inputArgs: [],
outputArgs: [],
})),
container,
hardwareDecoding: false,
shouldProcess: false,
overallInputArguments: [],
overallOuputArguments: [],
};
args.variables.ffmpegCommand = ffmpegCommand;