diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 3d80d30ac..3cf9b0fe0 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -28,7 +28,8 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - run: npm i + - run: npm i + - run: npm i -g typescript && tsc - run: npm run checkPlugins - run: npm run lint - run: npm run test diff --git a/.gitignore b/.gitignore index 32d518cec..1c4bf3485 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ ### VS Code ### .vscode/ + +/Local +/FlowPluginsTs/LocalFlowPlugins +/FlowPlugins/LocalFlowPlugins \ No newline at end of file diff --git a/Community/Tdarr_Plugin_MC93_Migz1Remux.js b/Community/Tdarr_Plugin_MC93_Migz1Remux.js index 275689ed8..4d0372351 100644 --- a/Community/Tdarr_Plugin_MC93_Migz1Remux.js +++ b/Community/Tdarr_Plugin_MC93_Migz1Remux.js @@ -136,8 +136,13 @@ const plugin = (file, librarySettings, inputs, otherArguments) => { return response; } - // If Container .ts or .avi set genpts to fix unknown timestamp - if (file.container.toLowerCase() === 'ts' || file.container.toLowerCase() === 'avi') { + // If Container .ts|.avi|.mpg|.mpeg set genpts to fix unknown timestamp + if ( + file.container.toLowerCase() === 'ts' + || file.container.toLowerCase() === 'avi' + || file.container.toLowerCase() === 'mpg' + || file.container.toLowerCase() === 'mpeg' + ) { genpts = '-fflags +genpts'; } diff --git a/Community/Tdarr_Plugin_a9he_New_file_size_check.js b/Community/Tdarr_Plugin_a9he_New_file_size_check.js index f1dbb9afb..a03571336 100644 --- a/Community/Tdarr_Plugin_a9he_New_file_size_check.js +++ b/Community/Tdarr_Plugin_a9he_New_file_size_check.js @@ -17,7 +17,7 @@ const details = () => ({ }, tooltip: `Enter the upper bound % size for the new file. For example, if '110' is entered, - then if the new file size is 11% larger than the original, an error will be given.`, + then if the new file size is greater than 110% the size of the original, an error will be given.`, }, { name: 'lowerBound', diff --git a/Community/Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js b/Community/Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js index 001bcb7ac..4d462242d 100644 --- a/Community/Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js +++ b/Community/Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js @@ -606,7 +606,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => { // With this set we also disable hardware decode for compatibility later if (inputs.enable_10bit === true) { main10 = true; - extraArguments += '-profile:v main10 -pix_fmt p010le '; + extraArguments += '-profile:v main10 -vf scale_qsv=format=p010le '; response.infoLog += '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format \n'; } diff --git a/Community/Tdarr_Plugin_scha_rename_based_on_codec_schadi.js b/Community/Tdarr_Plugin_scha_rename_based_on_codec_schadi.js new file mode 100644 index 000000000..aab6e8144 --- /dev/null +++ b/Community/Tdarr_Plugin_scha_rename_based_on_codec_schadi.js @@ -0,0 +1,206 @@ +/* eslint-disable max-len */ + +// tdarrSkipTest +const details = () => ({ + id: 'Tdarr_Plugin_scha_rename_based_on_codec_schadi', + Stage: 'Post-processing', + Name: 'Rename based on codec Video and Audio', + Type: 'Video', + Operation: 'Transcode', + Description: ` + If the filename contains a codec information like h264, av1 or similar for video and AC3, AAC or trueHD \n\n + the plugin will read the codec info from the file and rename it accordingly. \n\n + It also takes care off addiotnal files deffined in the input Option.\n\n`, + Version: '1.00', + Tags: 'post-processing', + Inputs: [ + { + name: 'rename_audio', + type: 'boolean', + defaultValue: false, + inputUI: { + type: 'dropdown', + options: [ + 'false', + 'true', + ], + }, + tooltip: `Will Rename According to Audio Codec after x264 or x265. + \\nExample:\\n + true + \\nExample:\\n + false`, + }, + { + name: 'rename_video', + type: 'boolean', + defaultValue: false, + inputUI: { + type: 'dropdown', + options: [ + 'false', + 'true', + ], + }, + tooltip: `Will Rename According to Video Codec after x264 or x265. + \\nExample:\\n + true + \\nExample:\\n + false`, + }, + { + name: 'additional_extensions', + type: 'string', + defaultValue: '.nfo,.srt', + inputUI: { + type: 'text', + }, + tooltip: `Additional file extensions to rename (comma-separated). + \\nExample:\\n + .nfo,.srt`, + }, + ], +}); + +// eslint-disable-next-line no-unused-vars +const plugin = (file, librarySettings, inputs, otherArguments) => { + const lib = require('../methods/lib')(); + // eslint-disable-next-line no-unused-vars,no-param-reassign + inputs = lib.loadDefaultValues(inputs, details); + + const fs = require('fs'); + const path = require('path'); + + const fileNameOld = file._id; + + const response = { + file, + removeFromDB: false, + updateDB: true, + infoLog: '', + processFile: false, + }; + + const codecMap = { + aac: 'AAC', + ac3: 'AC3', + av1: 'AV1', + avc: 'h264', + dts: 'DTS', + eac3: 'EAC3', + flac: 'FLAC', + hevc: 'h265', + mp2: 'MP2', + mp3: 'MP3', + mpeg2: 'MPEG2', + truehd: 'TrueHD', + x264: 'h264', + x265: 'h265', + h264: 'h264', + h265: 'h265', + // dts: 'DTS-X', + 'dts-hd ma': 'DTS-HD MA', + 'dts-es': 'DTS-HD ES', + 'dts-hd hra': 'DTS-HD HRA', + 'dts express ': 'DTS Express', + 'dts 96/24': 'DTS', + }; + + let firstVideoStreamCodec; + let firstAudioStreamCodec; + + const videoCodecRegex = /(h264|h265|x264|x265|avc|hevc|mpeg2|av1)/gi; + const audioCodecRegex = /(aac|ac3|eac3|flac|mp2|mp3|truehd|dts[-. ]hd[-. ]ma|dts[-. ]hd[-. ]es|dts[-. ]hd[-. ]hra|dts[-. ]express|dts)/gi; + + const videoStream = file.ffProbeData.streams.find((stream) => stream.codec_type === 'video'); + + if (videoStream && inputs.rename_video) { + const videoCodec = videoStream.codec_name.toLowerCase(); + firstVideoStreamCodec = videoCodec; + + if (videoCodec in codecMap) { + const renamedCodec = codecMap[videoCodec]; + // eslint-disable-next-line no-param-reassign + file._id = file._id.replace(videoCodecRegex, renamedCodec); + // eslint-disable-next-line no-param-reassign + file.file = file.file.replace(videoCodecRegex, renamedCodec); + } + } + + const audioStream = file.ffProbeData.streams.find((stream) => stream.codec_type === 'audio'); + + if (audioStream && inputs.rename_audio) { + const audioCodec = audioStream.codec_name.toLowerCase(); + firstAudioStreamCodec = audioCodec; + + if (audioCodec in codecMap) { + const renamedCodec = codecMap[audioCodec]; + // eslint-disable-next-line no-param-reassign + file._id = file._id.replace(audioCodecRegex, renamedCodec); + // eslint-disable-next-line no-param-reassign + file.file = file.file.replace(audioCodecRegex, renamedCodec); + } + } + + let additionalFilesCount = 0; // Counter for additional files found + + if ((audioStream && inputs.rename_audio) || (videoStream && inputs.rename_video)) { + const filename = path.basename(fileNameOld); + const JustName = path.parse(filename).name; + const popJustnamen = JustName.split('.'); + popJustnamen.splice(popJustnamen.length - 5); + const modJustname = popJustnamen.join('.'); + + const fileDir = path.dirname(fileNameOld); + const directoryPath = fileDir; + + const additionalExtensions = inputs.additional_extensions.split(','); + + const fileList = []; // Array to store the file names + const files = fs.readdirSync(directoryPath); + + files.forEach((supportFile) => { + fileList.push(supportFile); // Add all files to the fileList array + }); + + const extensionList = additionalExtensions.map((extension) => extension.trim()); // Remove leading/trailing spaces from extensions + const regex = new RegExp(`(${extensionList.join('|')})$`, 'i'); + + files.forEach((supportFile) => { + if (supportFile.startsWith(modJustname) && regex.test(supportFile)) { + const renamedFileWithVideoCodec = supportFile.replace(videoCodecRegex, codecMap[firstVideoStreamCodec]); + const renamedFileWithBothCodecs = renamedFileWithVideoCodec.replace(audioCodecRegex, codecMap[firstAudioStreamCodec]); + + fs.renameSync(`${directoryPath}/${supportFile}`, `${directoryPath}/${renamedFileWithBothCodecs}`, { + overwrite: true, + }); + + response.infoLog += `${directoryPath}/${supportFile} renamed to ${directoryPath}/${renamedFileWithBothCodecs}\n`; + additionalFilesCount += 1; // Increment the count for each additional file found + } + }); + + // const textFilePath = path.join(directoryPath, `${modJustname}.txt`); + // fs.writeFileSync(textFilePath, fileList.filter(file => file.startsWith(modJustname) && regex.test(file)).join('\n'), 'utf-8'); + } + + if (fileNameOld !== file._id) { + fs.renameSync(fileNameOld, file._id, { + overwrite: true, + }); + response.infoLog += `Renamed file to: ${file._id}\n`; + if (additionalFilesCount > 0) { + response.infoLog += `and: ${additionalFilesCount} additional Files!\n`; + } + return response; + } + + response.infoLog += 'Video File not renamed!\n'; + if (additionalFilesCount > 0) { + response.infoLog += `But: ${additionalFilesCount} additional Files!\n`; + } + return response; +}; + +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_z80t_keep_original_date.js b/Community/Tdarr_Plugin_z80t_keep_original_date.js index 25c9a0210..ebdd36a56 100644 --- a/Community/Tdarr_Plugin_z80t_keep_original_date.js +++ b/Community/Tdarr_Plugin_z80t_keep_original_date.js @@ -1,6 +1,4 @@ module.exports.dependencies = [ - 'axios', - 'path-extra', 'touch', ]; @@ -14,66 +12,36 @@ const details = () => ({ Description: 'This plugin copies the original file dates and times to the transcoded file \n\n', Version: '1.10', Tags: 'post-processing,dates,date', - Inputs: [{ - name: 'server', - type: 'string', - defaultValue: '192.168.1.100', - inputUI: { - type: 'text', - }, - tooltip: `IP address or hostname of the server assigned to this node, will be used for API requests. - If you are running nodes within Docker you should use the server IP address rather than the name. - - \\nExample:\\n - tdarrserver - - \\nExample:\\n - 192.168.1.100`, - }, { - name: 'extensions', - type: 'string', - defaultValue: '', - inputUI: { - type: 'text', - }, - tooltip: `When files are trans-coded the file extension may change, - enter a list of extensions to try and match the original file with in the database after trans-coding. - Default is the list of container types from library settings. The list will be searched in order and - the extension of the original file will always be checked first before the list is used. - - \\nExample:\\n - mkv,mp4,avi`, - }, - { - name: 'log', - type: 'boolean', - defaultValue: false, - inputUI: { - type: 'dropdown', - options: [ - 'false', - 'true', - ], - }, - tooltip: `Write log entries to console.log. Default is false. + Inputs: [ + { + name: 'log', + type: 'boolean', + defaultValue: false, + inputUI: { + type: 'dropdown', + options: [ + 'false', + 'true', + ], + }, + tooltip: `Write log entries to console.log. Default is false. \\nExample:\\n true`, - }, + }, ], }); // eslint-disable-next-line @typescript-eslint/no-unused-vars -const plugin = async (file, librarySettings, inputs, otherArguments) => { +const plugin = (file, librarySettings, inputs, otherArguments) => { const lib = require('../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign inputs = lib.loadDefaultValues(inputs, details); - // eslint-disable-next-line import/no-unresolved - const axios = require('axios'); - // eslint-disable-next-line import/no-unresolved + + // eslint-disable-next-line import/no-unresolved,import/no-extraneous-dependencies const touch = require('touch'); - // eslint-disable-next-line import/no-unresolved - const path = require('path-extra'); + const os = require('os'); + const fs = require('fs'); const log = (msg) => { if (inputs.log === true) { @@ -82,40 +50,6 @@ const plugin = async (file, librarySettings, inputs, otherArguments) => { } }; - const getFileData = async (filePath, extensions, server) => { - const originalExtension = path.extname(filePath).split('.')[1]; - if (extensions.indexOf(originalExtension) > -1) { - extensions.splice(extensions.indexOf(originalExtension), 1); - } - extensions.unshift(originalExtension); - let httpResponse = null; - - for (let i = 0; i < extensions.length; i += 1) { - const fileName = path.replaceExt(filePath, `.${extensions[i]}`); - log(`Fetching file object for ${fileName}...`); - // eslint-disable-next-line no-await-in-loop - httpResponse = await axios.post(`http://${server}:8265/api/v2/search-db`, { - data: { - string: fileName, - lessThanGB: 10000, - greaterThanGB: 0, - }, - }); - - if (httpResponse.status === 200) { - if (httpResponse.data.length > 0) { - log(`Got response for ${fileName}`); - return httpResponse; - } - log(`Response for ${fileName} is empty`); - } else { - log(`API request for ${filePath} failed.`); - } - } - log('Could not get file info from API, giving up.'); - return httpResponse; - }; - const responseData = { file, removeFromDB: false, @@ -124,30 +58,26 @@ const plugin = async (file, librarySettings, inputs, otherArguments) => { }; try { - if (!inputs.server || inputs.server.trim() === '') { - responseData.infoLog += 'Tdarr server name/IP not configured in library transcode options\n'; - return responseData; - } - - log('Waiting 5 seconds...'); - - let { extensions } = inputs; - if (!extensions || extensions.trim() === '') { - extensions = librarySettings.containerFilter; + log('Changing date...'); + + const { mtimeMs } = otherArguments.originalLibraryFile.statSync; + if (os.platform() === 'win32') { + fs.utimes( + file._id, + new Date().getTime() / 1000, + mtimeMs / 1000, + (err) => { + if (err) { + log('Error updating modified date'); + } + }, + ); + } else { + touch.sync(file._id, { mtimeMs, force: true }); } - extensions = extensions.split(','); - - await new Promise((resolve) => setTimeout(resolve, 5000)); - const response = await getFileData(file._id, extensions, inputs.server); - if (response.data.length > 0) { - log('Changing date...'); - touch.sync(file._id, { time: Date.parse(response.data[0].statSync.mtime), force: true }); - log('Done.'); - responseData.infoLog += 'File timestamps updated or match original file\n'; - return responseData; - } - responseData.infoLog += `Could not find file using API using ${inputs.server}\n`; + log('Done.'); + responseData.infoLog += 'File timestamps updated or match original file\n'; return responseData; } catch (err) { log(err); diff --git a/FlowPlugins/CommunityFlowPlugins/audio/checkChannelCount/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/audio/checkChannelCount/1.0.0/index.js new file mode 100644 index 000000000..97d804131 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/audio/checkChannelCount/1.0.0/index.js @@ -0,0 +1,73 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Check Channel Count', + description: 'Check streams for specified channel count', + style: { + borderColor: 'orange', + }, + tags: 'audio', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'channelCount', + type: 'number', + defaultValue: '2', + inputUI: { + type: 'dropdown', + options: [ + '1', + '2', + '6', + '8', + ], + }, + tooltip: 'Specify channel count to check for', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File has stream with specified channel count', + }, + { + number: 2, + tooltip: 'File does not have stream with specified channel count', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var _a, _b; + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + var channelCount = Number(args.inputs.channelCount); + var hasSpecifiedChannelCount = false; + args.jobLog("Checking for ".concat(channelCount, " channels")); + if (Array.isArray((_b = (_a = args === null || args === void 0 ? void 0 : args.inputFileObj) === null || _a === void 0 ? void 0 : _a.ffProbeData) === null || _b === void 0 ? void 0 : _b.streams)) { + for (var i = 0; i < args.inputFileObj.ffProbeData.streams.length; i += 1) { + var stream = args.inputFileObj.ffProbeData.streams[i]; + args.jobLog("Stream ".concat(i, " has ").concat(stream.channels, " channels")); + if (stream.channels === channelCount) { + hasSpecifiedChannelCount = true; + } + } + } + else { + throw new Error('File has no stream data'); + } + return { + outputFileObj: args.inputFileObj, + outputNumber: hasSpecifiedChannelCount ? 1 : 2, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/audio/normalizeAudio/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/audio/normalizeAudio/1.0.0/index.js index c5a21c1c1..bd3d957ac 100644 --- a/FlowPlugins/CommunityFlowPlugins/audio/normalizeAudio/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/audio/normalizeAudio/1.0.0/index.js @@ -48,6 +48,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -100,7 +102,7 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function lra = args.inputs.lra; tp = args.inputs.tp; container = (0, fileUtils_1.getContainer)(args.inputFileObj._id); - outputFilePath = "".concat(args.workDir, "/tempFile_").concat(new Date().getTime(), ".").concat(container); + outputFilePath = "".concat((0, fileUtils_1.getPluginWorkDir)(args), "/").concat((0, fileUtils_1.getFileName)(args.inputFileObj._id), ".").concat(container); normArgs1 = [ '-i', args.inputFileObj._id, diff --git a/FlowPlugins/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.js index d2e5320e2..4e6227e80 100644 --- a/FlowPlugins/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.js @@ -37,7 +37,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { }; Object.defineProperty(exports, "__esModule", { value: true }); exports.plugin = exports.details = void 0; -var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); +var classicPlugins_1 = require("../../../../FlowHelpers/1.0.0/classicPlugins"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ name: 'Run Classic Filter Plugin', @@ -47,6 +47,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ @@ -75,57 +77,17 @@ var details = function () { return ({ exports.details = details; // eslint-disable-next-line @typescript-eslint/no-unused-vars var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { - var path, lib, pluginSourceId, parts, pluginSource, pluginId, relativePluginPath, absolutePath, classicPlugin, res, container, cacheFilePath, otherArguments, result, outputNumber; + var lib, outcome, result, outputNumber; return __generator(this, function (_a) { switch (_a.label) { case 0: - path = require('path'); lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); - pluginSourceId = String(args.inputs.pluginSourceId); - parts = pluginSourceId.split(':'); - pluginSource = parts[0]; - pluginId = parts[1]; - relativePluginPath = "../../../../../".concat(pluginSource, "/").concat(pluginId, ".js"); - absolutePath = path.resolve(__dirname, relativePluginPath); - if (!(pluginSource === 'Community')) return [3 /*break*/, 1]; - classicPlugin = args.deps.importFresh(relativePluginPath); - return [3 /*break*/, 3]; - case 1: return [4 /*yield*/, args.deps.axiosMiddleware('api/v2/read-plugin', { - plugin: { - id: pluginId, - source: pluginSource, - }, - })]; - case 2: - res = _a.sent(); - classicPlugin = args.deps.requireFromString(res.pluginRaw, absolutePath); - _a.label = 3; - case 3: - if (classicPlugin.details().Operation !== 'Filter') { - throw new Error("".concat('This plugin is meant for classic plugins that have ' - + 'Operation: Filter. This classic plugin has Operation: ').concat(classicPlugin.details().Operation) - + 'Please use the Run Classic Transcode Flow Plugin plugin instead.'); - } - container = (0, fileUtils_1.getContainer)(args.inputFileObj._id); - cacheFilePath = "".concat(args.workDir, "/tempFile_").concat(new Date().getTime(), ".").concat(container); - otherArguments = { - handbrakePath: args.handbrakePath, - ffmpegPath: args.ffmpegPath, - mkvpropeditPath: args.mkvpropeditPath, - originalLibraryFile: args.originalLibraryFile, - nodeHardwareType: args.nodeHardwareType, - pluginCycle: 0, - workerType: args.workerType, - version: args.config.version, - platform_arch_isdocker: args.platform_arch_isdocker, - cacheFilePath: cacheFilePath, - job: args.job, - }; - return [4 /*yield*/, classicPlugin.plugin(args.inputFileObj, args.librarySettings, args.inputs, otherArguments)]; - case 4: - result = _a.sent(); + return [4 /*yield*/, (0, classicPlugins_1.runClassicPlugin)(args, 'filter')]; + case 1: + outcome = _a.sent(); + result = outcome.result; args.jobLog(JSON.stringify(result, null, 2)); outputNumber = (result === null || result === void 0 ? void 0 : result.processFile) ? 1 : 2; return [2 /*return*/, { diff --git a/FlowPlugins/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.js index 26ff99962..5ae18c6af 100644 --- a/FlowPlugins/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.js @@ -47,7 +47,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { Object.defineProperty(exports, "__esModule", { value: true }); exports.plugin = exports.details = void 0; var cliUtils_1 = require("../../../../FlowHelpers/1.0.0/cliUtils"); -var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); +var classicPlugins_1 = require("../../../../FlowHelpers/1.0.0/classicPlugins"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ name: 'Run Classic Transcode Plugin', @@ -57,6 +57,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -86,58 +88,19 @@ var replaceContainer = function (filePath, container) { }; // eslint-disable-next-line @typescript-eslint/no-unused-vars var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { - var path, lib, pluginSourceId, parts, pluginSource, pluginId, relativePluginPath, absolutePath, classicPlugin, res_1, container, cacheFilePath, otherArguments, result, cliPath_1, customArgs, isCustomConfig, presetSplit, workerCommand, cliPath, cli, res; + var lib, outcome, result, absolutePath, cacheFilePath, cliPath_1, customArgs, isCustomConfig, presetSplit, workerCommand, cliPath, cli, res; var _a, _b, _c; return __generator(this, function (_d) { switch (_d.label) { case 0: - path = require('path'); lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); - pluginSourceId = String(args.inputs.pluginSourceId); - parts = pluginSourceId.split(':'); - pluginSource = parts[0]; - pluginId = parts[1]; - relativePluginPath = "../../../../../".concat(pluginSource, "/").concat(pluginId, ".js"); - absolutePath = path.resolve(__dirname, relativePluginPath); - if (!(pluginSource === 'Community')) return [3 /*break*/, 1]; - classicPlugin = args.deps.importFresh(relativePluginPath); - return [3 /*break*/, 3]; - case 1: return [4 /*yield*/, args.deps.axiosMiddleware('api/v2/read-plugin', { - plugin: { - id: pluginId, - source: pluginSource, - }, - })]; - case 2: - res_1 = _d.sent(); - classicPlugin = args.deps.requireFromString(res_1.pluginRaw, absolutePath); - _d.label = 3; - case 3: - if (classicPlugin.details().Operation === 'Filter') { - throw new Error("".concat('This plugin is meant for classic plugins that have ' - + 'Operation: Transcode. This classic plugin has Operation: ').concat(classicPlugin.details().Operation) - + 'Please use the Run Classic Filter Flow Plugin plugin instead.'); - } - container = (0, fileUtils_1.getContainer)(args.inputFileObj._id); - cacheFilePath = "".concat(args.workDir, "/tempFile_").concat(new Date().getTime(), ".").concat(container); - otherArguments = { - handbrakePath: args.handbrakePath, - ffmpegPath: args.ffmpegPath, - mkvpropeditPath: args.mkvpropeditPath, - originalLibraryFile: args.originalLibraryFile, - nodeHardwareType: args.nodeHardwareType, - pluginCycle: 0, - workerType: args.workerType, - version: args.config.version, - platform_arch_isdocker: args.platform_arch_isdocker, - cacheFilePath: cacheFilePath, - job: args.job, - }; - return [4 /*yield*/, classicPlugin.plugin(args.inputFileObj, args.librarySettings, args.inputs, otherArguments)]; - case 4: - result = _d.sent(); + return [4 /*yield*/, (0, classicPlugins_1.runClassicPlugin)(args, 'transcode')]; + case 1: + outcome = _d.sent(); + result = outcome.result, absolutePath = outcome.absolutePath; + cacheFilePath = outcome.cacheFilePath; args.jobLog(JSON.stringify(result, null, 2)); if (!result) { args.jobLog('No result from classic plugin. Continuing to next flow plugin.'); @@ -190,11 +153,15 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function } customArgs = (_b = result === null || result === void 0 ? void 0 : result.custom) === null || _b === void 0 ? void 0 : _b.args; isCustomConfig = (Array.isArray(customArgs) && customArgs.length > 0) - || (typeof customArgs === 'string' && customArgs.length > 0); + || (typeof customArgs === 'string' + // @ts-expect-error length + && customArgs.length + > 0); if (!isCustomConfig) { cacheFilePath = replaceContainer(cacheFilePath, result.container); } else { + // @ts-expect-error type cacheFilePath = result.custom.outputPath; } if (result.preset.includes('')) { @@ -206,6 +173,7 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function workerCommand = []; cliPath = ''; if (isCustomConfig) { + // @ts-expect-error cliPath cliPath = (_c = result === null || result === void 0 ? void 0 : result.custom) === null || _c === void 0 ? void 0 : _c.cliPath; if (Array.isArray(customArgs)) { workerCommand = customArgs; @@ -250,7 +218,7 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function updateWorker: args.updateWorker, }); return [4 /*yield*/, cli.runCli()]; - case 5: + case 2: res = _d.sent(); if (res.cliExitCode !== 0) { args.jobLog("Running ".concat(cliPath, " failed")); diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.js index 265007256..95cec298f 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.js index af2727a93..20439f7e3 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCustomArguments/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCustomArguments/1.0.0/index.js index 5f3069170..d88dcdb33 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCustomArguments/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCustomArguments/1.0.0/index.js @@ -4,16 +4,36 @@ exports.plugin = exports.details = void 0; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ name: 'Custom Arguments', - description: 'Custom Arguments', + description: 'Set FFmpeg custome input and output arguments', style: { borderColor: '#6efefc', - opacity: 0.5, }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', - inputs: [], + inputs: [ + { + name: 'inputArguments', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Specify input arguments', + }, + { + name: 'outputArguments', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Specify output arguments', + }, + ], outputs: [ { number: 1, @@ -24,9 +44,18 @@ var details = function () { return ({ exports.details = details; // eslint-disable-next-line @typescript-eslint/no-unused-vars var plugin = function (args) { + var _a, _b; var lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); + var inputArguments = String(args.inputs.inputArguments); + var outputArguments = String(args.inputs.outputArguments); + if (inputArguments) { + (_a = args.variables.ffmpegCommand.overallInputArguments).push.apply(_a, inputArguments.split(' ')); + } + if (outputArguments) { + (_b = args.variables.ffmpegCommand.overallOuputArguments).push.apply(_b, outputArguments.split(' ')); + } return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandEnsureAudioStream/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandEnsureAudioStream/1.0.0/index.js index 0808373ef..11a40664f 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandEnsureAudioStream/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandEnsureAudioStream/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.js index 9698d8fd0..cd9ac1fff 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.js @@ -47,6 +47,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { Object.defineProperty(exports, "__esModule", { value: true }); exports.plugin = exports.details = void 0; var cliUtils_1 = require("../../../../FlowHelpers/1.0.0/cliUtils"); +var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ name: 'Execute', @@ -56,6 +57,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: 2, icon: 'faPlay', inputs: [], @@ -104,7 +107,7 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function cliArgs.push('-y'); cliArgs.push('-i'); cliArgs.push(args.inputFileObj._id); - inputArgs = []; + inputArgs = __spreadArray([], args.variables.ffmpegCommand.overallInputArguments, true); _a = args.variables.ffmpegCommand, shouldProcess = _a.shouldProcess, streams = _a.streams; streams = streams.filter(function (stream) { if (stream.removed) { @@ -147,7 +150,9 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function } idx = cliArgs.indexOf('-i'); cliArgs.splice.apply(cliArgs, __spreadArray([idx, 0], inputArgs, false)); - outputFilePath = "".concat(args.workDir, "/tempFile_").concat(new Date().getTime(), ".").concat(args.variables.ffmpegCommand.container); + cliArgs.push.apply(cliArgs, args.variables.ffmpegCommand.overallOuputArguments); + outputFilePath = "".concat((0, fileUtils_1.getPluginWorkDir)(args), "/").concat((0, fileUtils_1.getFileName)(args.inputFileObj._id)) + + ".".concat(args.variables.ffmpegCommand.container); cliArgs.push(outputFilePath); args.jobLog('Processing file'); args.jobLog(JSON.stringify({ diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.js index 2e45becba..6cda6f2b0 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.js index 424b8fdbc..26fcf9ca4 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.js index 97b0d1c89..dea876d78 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.js index 7413f8eef..49b4a0b9f 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.js index 54e675c82..e1aa8f6d8 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.js index d76a9c977..6065dfc80 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.js @@ -12,6 +12,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -28,6 +30,19 @@ var details = function () { return ({ }, tooltip: 'Specify the container to use', }, + { + name: 'forceConform', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'dropdown', + options: [ + 'false', + 'true', + ], + }, + tooltip: "\nSpecify if you want to force conform the file to the new container,\nThis is useful if not all streams are supported by the new container. \nFor example mkv does not support data streams.\n ", + }, ], outputs: [ { @@ -43,9 +58,42 @@ var plugin = function (args) { // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); var newContainer = String(args.inputs.container); - if ((0, fileUtils_1.getContainer)(args.inputFileObj._id) !== args.inputs.container) { + var forceConform = args.inputs.forceConform; + if ((0, fileUtils_1.getContainer)(args.inputFileObj._id) !== newContainer) { args.variables.ffmpegCommand.container = newContainer; args.variables.ffmpegCommand.shouldProcess = true; + if (forceConform === true) { + for (var i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { + var stream = args.variables.ffmpegCommand.streams[i]; + try { + var codecType = stream.codec_type.toLowerCase(); + var codecName = stream.codec_name.toLowerCase(); + if (newContainer === 'mkv') { + if (codecType === 'data' + || [ + 'mov_text', + 'eia_608', + 'timed_id3', + ].includes(codecName)) { + stream.removed = true; + } + } + if (newContainer === 'mp4') { + if ([ + 'hdmv_pgs_subtitle', + 'eia_608', + 'timed_id3', + 'subrip', + ].includes(codecName)) { + stream.removed = true; + } + } + } + catch (err) { + // Error + } + } + } } return { outputFileObj: args.inputFileObj, diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoFramerate/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoFramerate/1.0.0/index.js new file mode 100644 index 000000000..210686e02 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoFramerate/1.0.0/index.js @@ -0,0 +1,78 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Set Video Framerate', + description: 'Set Video Framerate. If the original framerate is lower than the' + + ' specified framerate, the original framerate will be used.', + style: { + borderColor: '#6efefc', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'framerate', + type: 'number', + defaultValue: '30', + inputUI: { + type: 'text', + }, + tooltip: 'Specify framerate value', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + var desiredFrameRate = Number(args.inputs.framerate); + args.jobLog("Desired framerate: ".concat(desiredFrameRate)); + args.variables.ffmpegCommand.streams.forEach(function (stream) { + if (stream.codec_type === 'video') { + var fileFramerateUsed = false; + if (stream.avg_frame_rate) { + var parts = stream.avg_frame_rate.split('/'); + if (parts.length === 2) { + var numerator = parseInt(parts[0], 10); + var denominator = parseInt(parts[1], 10); + if (numerator > 0 && denominator > 0) { + var fileFramerate = numerator / denominator; + args.jobLog("File framerate: ".concat(fileFramerate)); + if (fileFramerate < desiredFrameRate) { + args.jobLog('File framerate is lower than desired framerate. Using file framerate.'); + stream.outputArgs.push('-r', "".concat(String(fileFramerate))); + fileFramerateUsed = true; + } + else { + args.jobLog('File framerate is greater than desired framerate. Using desired framerate.'); + } + } + } + } + if (!fileFramerateUsed) { + args.jobLog('Using desired framerate.'); + stream.outputArgs.push('-r', "".concat(String(desiredFrameRate))); + } + } + }); + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.js index 9295b9877..8606365ab 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.js index 61e49e33c..992a31f8f 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.js index 72e421c2e..45ab166ea 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.js @@ -48,6 +48,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -62,6 +64,7 @@ var details = function () { return ({ // 'vp9', 'h264', // 'vp8', + 'av1', ], }, tooltip: 'Specify codec of the output file', @@ -108,6 +111,22 @@ var details = function () { return ({ }, tooltip: 'Specify whether to use hardware encoding if available', }, + { + name: 'hardwareType', + type: 'string', + defaultValue: 'auto', + inputUI: { + type: 'dropdown', + options: [ + 'auto', + 'nvenc', + 'qsv', + 'vaapi', + 'videotoolbox', + ], + }, + tooltip: 'Specify codec of the output file', + }, { name: 'hardwareDecoding', type: 'boolean', @@ -124,7 +143,7 @@ var details = function () { return ({ { name: 'forceEncoding', type: 'boolean', - defaultValue: 'false', + defaultValue: 'true', inputUI: { type: 'dropdown', options: [ @@ -145,7 +164,7 @@ var details = function () { return ({ exports.details = details; // eslint-disable-next-line @typescript-eslint/no-unused-vars var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { - var lib, hardwareDecoding, i, stream, targetCodec, ffmpegPreset, ffmpegQuality, forceEncoding, hardwarEncoding, encoderProperties; + var lib, hardwareDecoding, hardwareType, i, stream, targetCodec, ffmpegPreset, ffmpegQuality, forceEncoding, hardwarEncoding, encoderProperties; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { @@ -154,6 +173,7 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); hardwareDecoding = args.inputs.hardwareDecoding === true; + hardwareType = String(args.inputs.hardwareType); args.variables.ffmpegCommand.hardwareDecoding = hardwareDecoding; i = 0; _c.label = 1; @@ -172,6 +192,7 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function return [4 /*yield*/, (0, hardwareUtils_1.getEncoder)({ targetCodec: targetCodec, hardwareEncoding: hardwarEncoding, + hardwareType: hardwareType, args: args, })]; case 2: @@ -183,7 +204,7 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function else { stream.outputArgs.push('-crf', ffmpegQuality); } - if (ffmpegPreset) { + if (targetCodec !== 'av1' && ffmpegPreset) { stream.outputArgs.push('-preset', ffmpegPreset); } if (hardwareDecoding) { diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.js index 532b452b9..d00a4acd8 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.js @@ -24,6 +24,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: 1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/file/checkFileExists/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/checkFileExists/1.0.0/index.js new file mode 100644 index 000000000..a7dcf0123 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/checkFileExists/1.0.0/index.js @@ -0,0 +1,82 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +var fs_1 = __importDefault(require("fs")); +var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Check File Exists', + description: 'Check file Exists', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'fileToCheck', + type: 'string', + // eslint-disable-next-line no-template-curly-in-string + defaultValue: '${fileName}_720p.${container}', + inputUI: { + type: 'text', + }, + // eslint-disable-next-line no-template-curly-in-string + tooltip: 'Specify file to check using templating e.g. ${fileName}_720p.${container}', + }, + { + name: 'directory', + type: 'string', + defaultValue: '', + inputUI: { + type: 'directory', + }, + tooltip: 'Specify directory to check. Leave blank to use working directory.' + + ' Put below Input File plugin to check original file directory.', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File exists', + }, + { + number: 2, + tooltip: 'File does not exist', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + var directory = String(args.inputs.directory).trim() || (0, fileUtils_1.getFileAbosluteDir)(args.inputFileObj._id); + var fileName = (0, fileUtils_1.getFileName)(args.inputFileObj._id); + var fileToCheck = String(args.inputs.fileToCheck).trim(); + fileToCheck = fileToCheck.replace(/\${fileName}/g, fileName); + fileToCheck = fileToCheck.replace(/\${container}/g, (0, fileUtils_1.getContainer)(args.inputFileObj._id)); + fileToCheck = "".concat(directory, "/").concat(fileToCheck); + var fileExists = false; + if (fs_1.default.existsSync(fileToCheck)) { + fileExists = true; + args.jobLog("File exists: ".concat(fileToCheck)); + } + else { + args.jobLog("File does not exist: ".concat(fileToCheck)); + } + return { + outputFileObj: args.inputFileObj, + outputNumber: fileExists ? 1 : 2, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.js index 72f6c600b..1311d3a40 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ diff --git a/FlowPlugins/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.js index 7f0c9ecdc..dd6095258 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/file/checkFileNameIncludes/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/checkFileNameIncludes/1.0.0/index.js new file mode 100644 index 000000000..692f6b717 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/checkFileNameIncludes/1.0.0/index.js @@ -0,0 +1,63 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Check File Name Includes', + description: 'Check if a file name includes specific terms. Only needs to match one term', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'terms', + type: 'string', + // eslint-disable-next-line no-template-curly-in-string + defaultValue: '_720p,_1080p', + inputUI: { + type: 'text', + }, + // eslint-disable-next-line no-template-curly-in-string + tooltip: 'Specify terms to check for in file name using comma seperated list e.g. _720p,_1080p', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File name contains terms', + }, + { + number: 2, + tooltip: 'File name does not contains terms', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + var fileName = "".concat((0, fileUtils_1.getFileName)(args.inputFileObj._id), ".").concat((0, fileUtils_1.getContainer)(args.inputFileObj._id)); + var terms = String(args.inputs.terms).trim().split(','); + var containsTerms = false; + for (var i = 0; i < terms.length; i++) { + if (fileName.includes(terms[i])) { + containsTerms = true; + break; + } + } + return { + outputFileObj: args.inputFileObj, + outputNumber: containsTerms ? 1 : 2, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.js index 620c13b7b..72dcfd709 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ diff --git a/FlowPlugins/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.js index dfbf12e24..e403445b7 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/file/compareFileSizeRatio/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/compareFileSizeRatio/1.0.0/index.js new file mode 100644 index 000000000..ea8b567ec --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/compareFileSizeRatio/1.0.0/index.js @@ -0,0 +1,84 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Compare File Size Ratio', + description: 'Compare file size ratio of working file compared to original file using percentage.', + style: { + borderColor: 'orange', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'greaterThan', + type: 'number', + defaultValue: '40', + inputUI: { + type: 'text', + }, + tooltip: 'Specify lower bound. ' + + 'Default value is 40% so new file size must be at least 40% of original file size.', + }, + { + name: 'lessThan', + type: 'number', + defaultValue: '110', + inputUI: { + type: 'text', + }, + tooltip: 'Specify upper bound.' + + ' Default value is 110% so new file size must be at most 110% of original file size.', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Working file size % is within range', + }, + { + number: 2, + tooltip: 'Working file size % is not within range', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + var isWithinRange = false; + var newFileSizeBytes = args.inputFileObj.file_size; + var origFileSizeBytes = args.originalLibraryFile.file_size; + var greaterThanPerc = Number(args.inputs.greaterThan); + var lessThanPerc = Number(args.inputs.lessThan); + var ratio = (newFileSizeBytes / origFileSizeBytes) * 100; + var sizeText = "New file has size ".concat(newFileSizeBytes.toFixed(3), " MB which is ").concat(ratio, "% ") + + "of original file size: ".concat(origFileSizeBytes.toFixed(3), " MB"); + var getBound = function (bound) { return (bound / 100) * origFileSizeBytes; }; + var errText = 'New file size not within limits.'; + if (newFileSizeBytes > getBound(lessThanPerc)) { + // Item will be errored in UI + args.jobLog("".concat(errText, " ").concat(sizeText, ". upperBound is ").concat(lessThanPerc, "%")); + } + else if (newFileSizeBytes < getBound(greaterThanPerc)) { + // // Item will be errored in UI + args.jobLog("".concat(errText, " ").concat(sizeText, ". lowerBound is ").concat(greaterThanPerc, "%")); + } + else { + args.jobLog(sizeText); + isWithinRange = true; + } + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/file/compareFileSizeRatio/2.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/compareFileSizeRatio/2.0.0/index.js new file mode 100644 index 000000000..7e03b9fe6 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/compareFileSizeRatio/2.0.0/index.js @@ -0,0 +1,89 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Compare File Size Ratio', + description: 'Compare file size ratio of working file compared to original file using percentage.', + style: { + borderColor: 'orange', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'greaterThan', + type: 'number', + defaultValue: '40', + inputUI: { + type: 'text', + }, + tooltip: 'Specify lower bound.' + + 'Default value is 40% so new file size must be at least 40% of original file size.', + }, + { + name: 'lessThan', + type: 'number', + defaultValue: '110', + inputUI: { + type: 'text', + }, + tooltip: 'Specify upper bound.' + + ' Default value is 110% so new file size must be at most 110% of original file size.', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Working file size % is within range', + }, + { + number: 2, + tooltip: 'Working file size % is smaller than lower bound', + }, + { + number: 3, + tooltip: 'Working file size % is larger than upper bound', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + var newFileSizeBytes = args.inputFileObj.file_size; + var origFileSizeBytes = args.originalLibraryFile.file_size; + var greaterThanPerc = Number(args.inputs.greaterThan); + var lessThanPerc = Number(args.inputs.lessThan); + var ratio = (newFileSizeBytes / origFileSizeBytes) * 100; + var sizeText = "New file has size ".concat(newFileSizeBytes.toFixed(3), " MB which is ").concat(ratio, "% ") + + "of original file size: ".concat(origFileSizeBytes.toFixed(3), " MB"); + var getBound = function (bound) { return (bound / 100) * origFileSizeBytes; }; + var outputNumber = 1; + var errText = 'New file size not within limits.'; + if (newFileSizeBytes > getBound(lessThanPerc)) { + // Item will be errored in UI + args.jobLog("".concat(errText, " ").concat(sizeText, ". upperBound is ").concat(lessThanPerc, "%")); + outputNumber = 3; + } + else if (newFileSizeBytes < getBound(greaterThanPerc)) { + // // Item will be errored in UI + args.jobLog("".concat(errText, " ").concat(sizeText, ". lowerBound is ").concat(greaterThanPerc, "%")); + outputNumber = 2; + } + else { + args.jobLog(sizeText); + } + return { + outputFileObj: args.inputFileObj, + outputNumber: outputNumber, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.js index 23cb848fa..545a96a2f 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.js @@ -52,6 +52,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [ @@ -69,7 +71,7 @@ var details = function () { return ({ type: 'boolean', defaultValue: 'false', inputUI: { - type: 'text', + type: 'dropdown', options: [ 'false', 'true', @@ -142,6 +144,16 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function } args.jobLog("Input path: ".concat(args.inputFileObj._id)); args.jobLog("Output path: ".concat(outputPath)); + if (args.inputFileObj._id === ouputFilePath) { + args.jobLog('Input and output path are the same, skipping copy.'); + return [2 /*return*/, { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }]; + } args.deps.fsextra.ensureDirSync(outputPath); return [4 /*yield*/, fs_1.promises.copyFile(args.inputFileObj._id, ouputFilePath)]; case 1: diff --git a/FlowPlugins/CommunityFlowPlugins/file/copyToWorkDirectory/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/copyToWorkDirectory/1.0.0/index.js index 636fedb14..a586cdc4a 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/copyToWorkDirectory/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/copyToWorkDirectory/1.0.0/index.js @@ -53,6 +53,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [], @@ -85,6 +87,16 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function }); args.jobLog("Input path: ".concat(args.inputFileObj._id)); args.jobLog("Output path: ".concat(outputPath)); + if (args.inputFileObj._id === ouputFilePath) { + args.jobLog('Input and output path are the same, skipping copy.'); + return [2 /*return*/, { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }]; + } args.deps.fsextra.ensureDirSync(outputPath); return [4 /*yield*/, fs_1.promises.copyFile(args.inputFileObj._id, ouputFilePath)]; case 1: diff --git a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.js index 7525c8f3c..f5c7446f1 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js index 82b22e04a..78954bdc2 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js @@ -51,6 +51,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [ @@ -68,7 +70,7 @@ var details = function () { return ({ type: 'boolean', defaultValue: 'false', inputUI: { - type: 'text', + type: 'dropdown', options: [ 'false', 'true', @@ -124,6 +126,16 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function }); args.jobLog("Input path: ".concat(args.inputFileObj._id)); args.jobLog("Output path: ".concat(ouputFilePath)); + if (args.inputFileObj._id === ouputFilePath) { + args.jobLog('Input and output path are the same, skipping move.'); + return [2 /*return*/, { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }]; + } args.deps.fsextra.ensureDirSync(outputPath); return [4 /*yield*/, (0, fileUtils_1.moveFileAndValidate)({ inputPath: args.inputFileObj._id, diff --git a/FlowPlugins/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.js new file mode 100644 index 000000000..06e3df4d9 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.js @@ -0,0 +1,103 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Move To Original Directory', + description: 'Move working file original directory.', + style: { + borderColor: 'green', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faArrowRight', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { + var lib, fileName, container, outputDir, ouputFilePath; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + fileName = (0, fileUtils_1.getFileName)(args.inputFileObj._id); + container = (0, fileUtils_1.getContainer)(args.inputFileObj._id); + outputDir = (0, fileUtils_1.getFileAbosluteDir)(args.originalLibraryFile._id); + ouputFilePath = "".concat(outputDir, "/").concat(fileName, ".").concat(container); + if (args.inputFileObj._id === ouputFilePath) { + args.jobLog('Input and output path are the same, skipping move.'); + return [2 /*return*/, { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }]; + } + return [4 /*yield*/, (0, fileUtils_1.moveFileAndValidate)({ + inputPath: args.inputFileObj._id, + outputPath: ouputFilePath, + args: args, + })]; + case 1: + _a.sent(); + return [2 /*return*/, { + outputFileObj: { + _id: ouputFilePath, + }, + outputNumber: 1, + variables: args.variables, + }]; + } + }); +}); }; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/file/renameFile/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/renameFile/1.0.0/index.js new file mode 100644 index 000000000..4fe65edad --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/renameFile/1.0.0/index.js @@ -0,0 +1,117 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Rename File', + description: 'Rename a file', + style: { + borderColor: 'green', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'fileRename', + type: 'string', + // eslint-disable-next-line no-template-curly-in-string + defaultValue: '${fileName}_720p.${container}', + inputUI: { + type: 'text', + }, + // eslint-disable-next-line no-template-curly-in-string + tooltip: 'Specify file to check using templating e.g. ${fileName}_720p.${container}', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { + var lib, fileName, newName, fileDir, newPath; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + fileName = (0, fileUtils_1.getFileName)(args.inputFileObj._id); + newName = String(args.inputs.fileRename).trim(); + newName = newName.replace(/\${fileName}/g, fileName); + newName = newName.replace(/\${container}/g, (0, fileUtils_1.getContainer)(args.inputFileObj._id)); + fileDir = (0, fileUtils_1.getFileAbosluteDir)(args.inputFileObj._id); + newPath = "".concat(fileDir, "/").concat(newName); + if (args.inputFileObj._id === newPath) { + args.jobLog('Input and output path are the same, skipping rename.'); + return [2 /*return*/, { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }]; + } + return [4 /*yield*/, (0, fileUtils_1.moveFileAndValidate)({ + inputPath: args.inputFileObj._id, + outputPath: newPath, + args: args, + })]; + case 1: + _a.sent(); + return [2 /*return*/, { + outputFileObj: { + _id: newPath, + }, + outputNumber: 1, + variables: args.variables, + }]; + } + }); +}); }; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js index ca1d8caed..4ec9bff01 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js @@ -41,12 +41,14 @@ var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ name: 'Replace Original File', - description: 'Replace the original file', + description: 'Replace the original file. If the file hasn\'t changed then no action is taken.', style: { borderColor: 'green', }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [], @@ -58,16 +60,9 @@ var details = function () { return ({ ], }); }; exports.details = details; -var getNewPath = function (originalPath, tempPath) { - var tempPathParts = tempPath.split('.'); - var container = tempPathParts[tempPathParts.length - 1]; - var originalPathParts = originalPath.split('.'); - originalPathParts[originalPathParts.length - 1] = container; - return originalPathParts.join('.'); -}; // eslint-disable-next-line @typescript-eslint/no-unused-vars var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { - var fs, lib, currentPath, newPath, newPathTmp; + var fs, lib, currentPath, orignalFolder, fileName, container, newPath, newPathTmp; return __generator(this, function (_a) { switch (_a.label) { case 0: @@ -86,7 +81,10 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function } args.jobLog('File has changed, replacing original file'); currentPath = args.inputFileObj._id; - newPath = getNewPath(args.originalLibraryFile._id, currentPath); + orignalFolder = (0, fileUtils_1.getFileAbosluteDir)(args.originalLibraryFile._id); + fileName = (0, fileUtils_1.getFileName)(args.inputFileObj._id); + container = (0, fileUtils_1.getContainer)(args.inputFileObj._id); + newPath = "".concat(orignalFolder, "/").concat(fileName, ".").concat(container); newPathTmp = "".concat(newPath, ".tmp"); args.jobLog(JSON.stringify({ currentPath: currentPath, @@ -96,10 +94,6 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })]; case 1: _a.sent(); - // delete temp file - if (fs.existsSync(newPath)) { - fs.unlinkSync(newPath); - } return [4 /*yield*/, (0, fileUtils_1.moveFileAndValidate)({ inputPath: currentPath, outputPath: newPathTmp, @@ -108,7 +102,9 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function case 2: _a.sent(); // delete original file - if (fs.existsSync(args.originalLibraryFile._id)) { + if (fs.existsSync(args.originalLibraryFile._id) + && args.originalLibraryFile._id !== currentPath) { + args.jobLog("Deleting original file:".concat(args.originalLibraryFile._id)); fs.unlinkSync(args.originalLibraryFile._id); } return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })]; diff --git a/FlowPlugins/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.js index 1ca5b52cf..4db30c296 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/file/unpack/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/unpack/1.0.0/index.js index a6210f3bf..d4bc94a3a 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/unpack/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/unpack/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.js index 066e77170..173d49d56 100644 --- a/FlowPlugins/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.js @@ -49,6 +49,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -112,7 +114,7 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function if (container === 'original') { container = (0, fileUtils_1.getContainer)(args.inputFileObj._id); } - outputFilePath = "".concat(args.workDir, "/tempFile_").concat(new Date().getTime(), ".").concat(container); + outputFilePath = "".concat((0, fileUtils_1.getPluginWorkDir)(args), "/").concat((0, fileUtils_1.getFileName)(args.inputFileObj._id), ".").concat(container); presetString = String(args.inputs.jsonPreset); cliArgs = [ '-i', diff --git a/FlowPlugins/CommunityFlowPlugins/input/inputFile/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/input/inputFile/1.0.0/index.js index 6f3fd72c1..ba0378d17 100644 --- a/FlowPlugins/CommunityFlowPlugins/input/inputFile/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/input/inputFile/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: true, + pType: 'start', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/tools/checkFlowVariable/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/checkFlowVariable/1.0.0/index.js new file mode 100644 index 000000000..737604a6d --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/tools/checkFlowVariable/1.0.0/index.js @@ -0,0 +1,125 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Check Flow Variable', + description: 'Check Flow Variable', + style: { + borderColor: 'orange', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'variable', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Variable to check. For example args.librarySettings._id', + }, + { + name: 'condition', + type: 'string', + defaultValue: '', + inputUI: { + type: 'dropdown', + options: [ + '==', + '!=', + ], + }, + tooltip: 'Check condition', + }, + { + name: 'value', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Value of variable to check', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'The variable matches the condition', + }, + { + number: 2, + tooltip: 'The variable does not match the condition', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + var variable = String(args.inputs.variable); + var condition = String(args.inputs.condition); + var value = String(args.inputs.value); + // variable could be e.g. args.librarySettings._id or args.inputFileObj._id + // condition could be e.g. '==' or '!=' + var variableParts = variable.split('.'); + var targetValue; + switch (variableParts.length) { + case 1: + targetValue = args; + break; + case 2: + // @ts-expect-error index + targetValue = args[variableParts[1]]; + break; + case 3: + // @ts-expect-error index + targetValue = args[variableParts[1]][variableParts[2]]; + break; + case 4: + // @ts-expect-error index + targetValue = args[variableParts[1]][variableParts[2]][variableParts[3]]; + break; + case 5: + // @ts-expect-error index + targetValue = args[variableParts[1]][variableParts[2]][variableParts[3]][variableParts[4]]; + break; + default: + throw new Error("Invalid variable: ".concat(variable)); + } + targetValue = String(targetValue); + var outputNumber = 1; + if (condition === '==') { + if (targetValue === value) { + args.jobLog("Variable ".concat(variable, " of value ").concat(targetValue, " matches condition ").concat(condition, " ").concat(value)); + outputNumber = 1; + } + else { + args.jobLog("Variable ".concat(variable, " of value ").concat(targetValue, " does not match condition ").concat(condition, " ").concat(value)); + outputNumber = 2; + } + } + else if (condition === '!=') { + if (targetValue !== value) { + args.jobLog("Variable ".concat(variable, " of value ").concat(targetValue, " matches condition ").concat(condition, " ").concat(value)); + outputNumber = 1; + } + else { + args.jobLog("Variable ".concat(variable, " of value ").concat(targetValue, " does not match condition ").concat(condition, " ").concat(value)); + outputNumber = 2; + } + } + return { + outputFileObj: args.inputFileObj, + outputNumber: outputNumber, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/checkNodeHardwareEncoder/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/checkNodeHardwareEncoder/1.0.0/index.js new file mode 100644 index 000000000..fbd3e377c --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/tools/checkNodeHardwareEncoder/1.0.0/index.js @@ -0,0 +1,112 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +var hardwareUtils_1 = require("../../../../FlowHelpers/1.0.0/hardwareUtils"); +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Check Node Hardware Encoder', + description: "\n Check if node hardware encoder is available. Can also be used to check for specific hardware.\n For example:\n\n hevc_nvenc = Nvidia\n hevc_amf = AMD\n hevc_vaapi = Intel\n hevc_qsv = Intel\n hevc_videotoolbox = Apple\n ", + style: { + borderColor: 'orange', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'hardwareEncoder', + type: 'string', + defaultValue: 'hevc_nvenc', + inputUI: { + type: 'dropdown', + options: [ + 'hevc_nvenc', + 'hevc_amf', + 'hevc_vaapi', + 'hevc_qsv', + 'hevc_videotoolbox', + ], + }, + tooltip: 'Specify hardware (based on encoder) to check for', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Node has hardware', + }, + { + number: 2, + tooltip: 'Node does not have hardware', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { + var lib, hardwareEncoder, encoderProperties, nodeHasHardware; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + hardwareEncoder = args.inputs.hardwareEncoder; + return [4 /*yield*/, (0, hardwareUtils_1.getEncoder)({ + targetCodec: 'hevc', + hardwareEncoding: true, + hardwareType: 'auto', + args: args, + })]; + case 1: + encoderProperties = _a.sent(); + nodeHasHardware = encoderProperties.enabledDevices.some(function (row) { return row.encoder === hardwareEncoder; }); + args.jobLog("Node has hardwareEncoder ".concat(hardwareEncoder, ": ").concat(nodeHasHardware)); + return [2 /*return*/, { + outputFileObj: args.inputFileObj, + outputNumber: nodeHasHardware ? 1 : 2, + variables: args.variables, + }]; + } + }); +}); }; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/comment/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/comment/1.0.0/index.js new file mode 100644 index 000000000..50cdaeac8 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/tools/comment/1.0.0/index.js @@ -0,0 +1,45 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Comment', + nameUI: { + type: 'textarea', + style: { + height: '250px', + }, + }, + description: "Add a comment to your flow. Can place anywhere and link together.\n Any file input into the comment will be passed straight through.", + style: { + borderColor: 'white', + borderRadius: '10px', + backgroundColor: '#043775', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faComment', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var 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, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/failFlow/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/failFlow/1.0.0/index.js index 2572c7f4c..471a3cef8 100644 --- a/FlowPlugins/CommunityFlowPlugins/tools/failFlow/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/tools/failFlow/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faExclamationTriangle', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.js index 88560dae9..33795c354 100644 --- a/FlowPlugins/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.js @@ -6,14 +6,26 @@ var details = function () { return ({ name: 'Go To Flow', description: 'Go to a different flow', style: { - borderColor: 'red', - opacity: 0.5, + borderColor: 'green', }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', - inputs: [], + inputs: [ + { + name: 'flowId', + type: 'string', + defaultValue: '', + inputUI: { + type: 'dropdown', + options: [], + }, + tooltip: 'Specify flow ID to go to', + }, + ], outputs: [], }); }; exports.details = details; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/onFlowError/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/onFlowError/1.0.0/index.js new file mode 100644 index 000000000..57603b559 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/tools/onFlowError/1.0.0/index.js @@ -0,0 +1,37 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'On Flow Error', + description: "Runs if an error occurs in this specific flow. \n Won't run if error occurs in after going to a different flow (unless that flow comes back to this one).", + style: { + borderColor: 'red', + }, + tags: '', + isStartPlugin: false, + pType: 'onFlowError', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faArrowRight', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var 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, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/requireReview/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/requireReview/1.0.0/index.js new file mode 100644 index 000000000..9f5c7777e --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/tools/requireReview/1.0.0/index.js @@ -0,0 +1,37 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Require Review', + description: "Makes the flow pause.\n The file will stay in the staging section on the Tdarr tab until the user clicks the \"Reviewed\" button.\n ", + style: { + borderColor: 'yellow', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faHand', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var 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, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/resetFlowError/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/resetFlowError/1.0.0/index.js new file mode 100644 index 000000000..fe9b528f6 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/tools/resetFlowError/1.0.0/index.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Reset Flow Error', + description: "After a flow error occurs, this plugin will reset the flow\nerror so that the flow will not go to error status at the end of the flow.", + style: { + borderColor: 'red', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faUndo', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + // eslint-disable-next-line no-param-reassign + args.variables.flowFailed = false; + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.js index d39a60860..16cd80601 100644 --- a/FlowPlugins/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.js @@ -48,6 +48,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPlugins/CommunityFlowPlugins/tools/waitTimeout/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/waitTimeout/1.0.0/index.js new file mode 100644 index 000000000..2ecd43e7d --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/tools/waitTimeout/1.0.0/index.js @@ -0,0 +1,133 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Wait', + description: 'Wait for a specified amount of time before continuing to the next plugin', + style: { + borderColor: 'yellow', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faClock', + inputs: [ + { + name: 'amount', + type: 'string', + defaultValue: '1', + inputUI: { + type: 'text', + }, + tooltip: 'Specify the amount of time to wait', + }, + { + name: 'unit', + type: 'string', + defaultValue: 'seconds', + inputUI: { + type: 'dropdown', + options: [ + 'seconds', + 'minutes', + 'hours', + ], + }, + tooltip: 'Specify the unit of time to wait', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { + var lib, _a, amount, unit, amountNum, multiplier, waitTime, finished, logWait; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + _a = args.inputs, amount = _a.amount, unit = _a.unit; + amountNum = Number(amount); + if (Number.isNaN(amountNum)) { + throw new Error('Amount must be a number'); + } + multiplier = 1; + if (unit === 'seconds') { + multiplier = 1000; + } + else if (unit === 'minutes') { + multiplier = 60000; + } + else if (unit === 'hours') { + multiplier = 3600000; + } + waitTime = amountNum * multiplier; + args.jobLog("Waiting for ".concat(amount, " ").concat(unit)); + args.jobLog("Waiting for ".concat(waitTime, " milliseconds")); + finished = false; + logWait = function () { + if (!finished) { + args.jobLog('Waiting...'); + setTimeout(logWait, 5000); + } + }; + logWait(); + return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, waitTime); })]; + case 1: + _b.sent(); + finished = true; + return [2 /*return*/, { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }]; + } + }); +}); }; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/webRequest/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/webRequest/1.0.0/index.js index 00f6e8b39..a20adff4f 100644 --- a/FlowPlugins/CommunityFlowPlugins/tools/webRequest/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/tools/webRequest/1.0.0/index.js @@ -1,4 +1,40 @@ "use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.plugin = exports.details = void 0; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ @@ -7,13 +43,63 @@ var details = function () { return ({ description: 'Send Web Request', style: { borderColor: 'green', - opacity: 0.5, }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', - inputs: [], + inputs: [ + { + name: 'method', + type: 'string', + defaultValue: 'post', + inputUI: { + type: 'dropdown', + options: [ + 'get', + 'post', + 'put', + 'delete', + ], + }, + tooltip: 'Specify request method', + }, + { + name: 'requestUrl', + type: 'string', + defaultValue: 'http://example.com', + inputUI: { + type: 'text', + }, + tooltip: 'Specify request URL', + }, + { + name: 'requestHeaders', + type: 'string', + defaultValue: "{\n \"Content-Type\": \"application/json\"\n}", + inputUI: { + type: 'textarea', + style: { + height: '100px', + }, + }, + tooltip: 'Specify request URL', + }, + { + name: 'requestBody', + type: 'string', + defaultValue: "{\n \"test\": \"test\"\n}", + inputUI: { + type: 'textarea', + style: { + height: '100px', + }, + }, + tooltip: 'Specify request body', + }, + ], outputs: [ { number: 1, @@ -23,14 +109,43 @@ var details = function () { return ({ }); }; exports.details = details; // eslint-disable-next-line @typescript-eslint/no-unused-vars -var plugin = function (args) { - var 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, - }; -}; +var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { + var lib, method, requestUrl, requestHeaders, requestBody, requestConfig, res, err_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + method = String(args.inputs.method); + requestUrl = String(args.inputs.requestUrl); + requestHeaders = JSON.parse(String(args.inputs.requestHeaders)); + requestBody = JSON.parse(String(args.inputs.requestBody)); + requestConfig = { + method: method, + url: requestUrl, + headers: requestHeaders, + data: requestBody, + }; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, args.deps.axios(requestConfig)]; + case 2: + res = _a.sent(); + args.jobLog("Web request succeeded: Status Code: ".concat(res.status)); + return [3 /*break*/, 4]; + case 3: + err_1 = _a.sent(); + args.jobLog('Web Request Failed'); + args.jobLog(JSON.stringify(err_1)); + throw new Error('Web Request Failed'); + case 4: return [2 /*return*/, { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }]; + } + }); +}); }; exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/video/CheckVideoFramerate/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/CheckVideoFramerate/1.0.0/index.js new file mode 100644 index 000000000..8fcc656c2 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/video/CheckVideoFramerate/1.0.0/index.js @@ -0,0 +1,79 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Check Video Framerate', + description: 'Check if video framerate is within a specific range', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'greaterThan', + type: 'number', + defaultValue: '0', + inputUI: { + type: 'text', + }, + tooltip: 'Specify lower bound of fps', + }, + { + name: 'lessThan', + type: 'number', + defaultValue: '60', + inputUI: { + type: 'text', + }, + tooltip: 'Specify upper bound fps', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File within range', + }, + { + number: 2, + tooltip: 'File not within range', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var _a, _b; + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + var isWithinRange = false; + var greaterThanFps = Number(args.inputs.greaterThan); + var lessThanFps = Number(args.inputs.lessThan); + var VideoFrameRate = (_b = (_a = args.inputFileObj) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.VideoFrameRate; + if (VideoFrameRate) { + if (VideoFrameRate >= greaterThanFps && VideoFrameRate <= lessThanFps) { + isWithinRange = true; + } + } + else { + throw new Error('Video framerate not found'); + } + if (isWithinRange) { + args.jobLog("Video framerate of ".concat(VideoFrameRate, " is within range of ").concat(greaterThanFps, " and ").concat(lessThanFps)); + } + else { + args.jobLog("Video framerate of ".concat(VideoFrameRate, " is not within range of ").concat(greaterThanFps, " and ").concat(lessThanFps)); + } + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/video/check10Bit/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/check10Bit/1.0.0/index.js index d436e355c..0c6c4ba98 100644 --- a/FlowPlugins/CommunityFlowPlugins/video/check10Bit/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/video/check10Bit/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], @@ -27,16 +29,24 @@ var details = function () { return ({ exports.details = details; // eslint-disable-next-line @typescript-eslint/no-unused-vars var plugin = function (args) { + var _a, _b; var lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); var is10Bit = false; - for (var i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { - var stream = args.variables.ffmpegCommand.streams[i]; - if (stream.codec_type === 'video' && stream.bits_per_raw_sample === 10) { - is10Bit = true; + if (Array.isArray((_b = (_a = args === null || args === void 0 ? void 0 : args.inputFileObj) === null || _a === void 0 ? void 0 : _a.ffProbeData) === null || _b === void 0 ? void 0 : _b.streams)) { + for (var i = 0; i < args.inputFileObj.ffProbeData.streams.length; i += 1) { + var stream = args.inputFileObj.ffProbeData.streams[i]; + if (stream.codec_type === 'video' + && (stream.bits_per_raw_sample === 10 + || stream.pix_fmt === 'yuv420p10le')) { + is10Bit = true; + } } } + else { + throw new Error('File has not stream data'); + } return { outputFileObj: args.inputFileObj, outputNumber: is10Bit ? 1 : 2, diff --git a/FlowPlugins/CommunityFlowPlugins/video/checkHdr/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/checkHdr/1.0.0/index.js index 286d38ff6..ca0617fb3 100644 --- a/FlowPlugins/CommunityFlowPlugins/video/checkHdr/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/video/checkHdr/1.0.0/index.js @@ -3,13 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.plugin = exports.details = void 0; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ - name: 'Check HDR', + name: 'Check HDR Video', description: 'Check if video is HDR', style: { borderColor: 'orange', }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], @@ -27,19 +29,25 @@ var details = function () { return ({ exports.details = details; // eslint-disable-next-line @typescript-eslint/no-unused-vars var plugin = function (args) { + var _a, _b; var lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); var isHdr = false; - for (var i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { - var stream = args.variables.ffmpegCommand.streams[i]; - if (stream.codec_type === 'video' - && stream.transfer_characteristics === 'smpte2084' - && stream.color_primaries === 'bt2020' - && stream.color_range === 'tv') { - isHdr = true; + if (Array.isArray((_b = (_a = args === null || args === void 0 ? void 0 : args.inputFileObj) === null || _a === void 0 ? void 0 : _a.ffProbeData) === null || _b === void 0 ? void 0 : _b.streams)) { + for (var i = 0; i < args.inputFileObj.ffProbeData.streams.length; i += 1) { + var stream = args.inputFileObj.ffProbeData.streams[i]; + if (stream.codec_type === 'video' + && stream.color_transfer === 'smpte2084' + && stream.color_primaries === 'bt2020' + && stream.color_range === 'tv') { + isHdr = true; + } } } + else { + throw new Error('File has not stream data'); + } return { outputFileObj: args.inputFileObj, outputNumber: isHdr ? 1 : 2, diff --git a/FlowPlugins/CommunityFlowPlugins/video/checkOverallBitrate/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/checkOverallBitrate/1.0.0/index.js new file mode 100644 index 000000000..7994cd37a --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/video/checkOverallBitrate/1.0.0/index.js @@ -0,0 +1,94 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'Check Overall Bitrate', + description: 'Check if overall file bitrate is within a specific range', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'unit', + type: 'string', + defaultValue: 'kbps', + inputUI: { + type: 'dropdown', + options: [ + 'bps', + 'kbps', + 'mbps', + ], + }, + tooltip: 'Specify the unit to use', + }, + { + name: 'greaterThan', + type: 'number', + defaultValue: '0', + inputUI: { + type: 'text', + }, + tooltip: 'Specify lower bound', + }, + { + name: 'lessThan', + type: 'number', + defaultValue: '10000', + inputUI: { + type: 'text', + }, + tooltip: 'Specify upper bound', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File within range', + }, + { + number: 2, + tooltip: 'File not within range', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + var isWithinRange = false; + var greaterThanBits = Number(args.inputs.greaterThan); + var lessThanBits = Number(args.inputs.lessThan); + if (args.inputs.unit === 'kbps') { + greaterThanBits *= 1000; + lessThanBits *= 1000; + } + else if (args.inputs.unit === 'mbps') { + greaterThanBits *= 1000000; + lessThanBits *= 1000000; + } + args.jobLog("File bitrate is ".concat(args.inputFileObj.bit_rate, " bps")); + args.jobLog("Checking if bitrate is within range ".concat(greaterThanBits, " bps and ").concat(lessThanBits, " bps")); + if (args.inputFileObj.bit_rate >= greaterThanBits && args.inputFileObj.bit_rate <= lessThanBits) { + isWithinRange = true; + args.jobLog('File bitrate is within range'); + } + else { + args.jobLog('File bitrate is not within range'); + } + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.js index 64f9bf91a..14959da32 100644 --- a/FlowPlugins/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ @@ -75,15 +77,23 @@ var plugin = function (args) { greaterThanBits *= 1000000; lessThanBits *= 1000000; } + var hasVideoBitrate = false; if ((_b = (_a = args.inputFileObj) === null || _a === void 0 ? void 0 : _a.mediaInfo) === null || _b === void 0 ? void 0 : _b.track) { args.inputFileObj.mediaInfo.track.forEach(function (stream) { - if (stream['@type'] === 'video') { + if (stream['@type'].toLowerCase() === 'video') { + if (stream.BitRate) { + hasVideoBitrate = true; + args.jobLog("Found video bitrate: ".concat(stream.BitRate)); + } if (stream.BitRate >= greaterThanBits && stream.BitRate <= lessThanBits) { isWithinRange = true; } } }); } + if (!hasVideoBitrate) { + throw new Error('Video bitrate not found'); + } return { outputFileObj: args.inputFileObj, outputNumber: isWithinRange ? 1 : 2, diff --git a/FlowPlugins/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.js index e245b74cb..bf6d14e2e 100644 --- a/FlowPlugins/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ diff --git a/FlowPlugins/CommunityFlowPlugins/video/checkVideoResolution/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/checkVideoResolution/1.0.0/index.js index bdbcfb242..bb965c448 100644 --- a/FlowPlugins/CommunityFlowPlugins/video/checkVideoResolution/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/video/checkVideoResolution/1.0.0/index.js @@ -10,6 +10,8 @@ var details = function () { return ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], @@ -58,9 +60,38 @@ var plugin = function (args) { var lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); + var outputNumber = 9; + switch (args.inputFileObj.video_resolution) { + case '480p': + outputNumber = 1; + break; + case '576p': + outputNumber = 2; + break; + case '720p': + outputNumber = 3; + break; + case '1080p': + outputNumber = 4; + break; + case '1440p': + outputNumber = 5; + break; + case '4KUHD': + outputNumber = 6; + break; + case 'DCI4K': + outputNumber = 7; + break; + case '8KUHD': + outputNumber = 8; + break; + default: + outputNumber = 9; + } return { outputFileObj: args.inputFileObj, - outputNumber: 1, + outputNumber: outputNumber, variables: args.variables, }; }; diff --git a/FlowPlugins/CommunityFlowPlugins/video/runHealthCheck/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/runHealthCheck/1.0.0/index.js new file mode 100644 index 000000000..7750de4ed --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/video/runHealthCheck/1.0.0/index.js @@ -0,0 +1,140 @@ +"use strict"; +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.plugin = exports.details = void 0; +var cliUtils_1 = require("../../../../FlowHelpers/1.0.0/cliUtils"); +var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); +/* eslint-disable no-param-reassign */ +var details = function () { return ({ + name: 'Run Health Check', + description: 'Run a quick health check using HandBrake or a thorough health check using FFmpeg', + style: { + borderColor: '#6efefc', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'type', + type: 'string', + defaultValue: 'quick', + inputUI: { + type: 'dropdown', + options: [ + 'quick', + 'thorough', + ], + }, + tooltip: 'Specify the container to use', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { + var lib, type, outputFilePath, cliPath, cliArgs, cli, res; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + type = String(args.inputs.type); + args.jobLog("Running health check of type ".concat(type)); + outputFilePath = "".concat((0, fileUtils_1.getPluginWorkDir)(args), "/").concat((0, fileUtils_1.getFileName)(args.inputFileObj._id)) + + ".".concat((0, fileUtils_1.getContainer)(args.inputFileObj._id)); + cliPath = args.handbrakePath; + cliArgs = [ + '-i', + args.inputFileObj._id, + '-o', + outputFilePath, + '--scan', + ]; + if (type === 'thorough') { + cliPath = args.ffmpegPath; + cliArgs = [ + '-stats', + '-v', + 'error', + '-i', + args.inputFileObj._id, + '-f', + 'null', + '-max_muxing_queue_size', + '9999', + outputFilePath, + ]; + } + cli = new cliUtils_1.CLI({ + cli: cliPath, + spawnArgs: cliArgs, + spawnOpts: {}, + jobLog: args.jobLog, + outputFilePath: outputFilePath, + inputFileObj: args.inputFileObj, + logFullCliOutput: args.logFullCliOutput, + updateWorker: args.updateWorker, + }); + return [4 /*yield*/, cli.runCli()]; + case 1: + res = _a.sent(); + if (res.cliExitCode !== 0) { + args.jobLog('Running CLI failed'); + throw new Error('Running CLI failed'); + } + return [2 /*return*/, { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }]; + } + }); +}); }; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/video/transcodeVideo/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/transcodeVideo/1.0.0/index.js index 8a3a2cfbe..f8eca12b9 100644 --- a/FlowPlugins/CommunityFlowPlugins/video/transcodeVideo/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/video/transcodeVideo/1.0.0/index.js @@ -11,6 +11,8 @@ var details = function () { return ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ diff --git a/FlowPlugins/CommunityFlowTemplates/tutorials/chapter1.js b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter1.js new file mode 100644 index 000000000..6f83c5ff9 --- /dev/null +++ b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter1.js @@ -0,0 +1,109 @@ +"use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; +var details = function () { return ({ + "name": "Chapter 1: Getting Started", + "description": "Chapter 1: Getting Started", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "_YTuyCZg3", + "position": { + "x": 644.7725474007168, + "y": -59.78556037646227 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "RQzydYbay", + "position": { + "x": 644.8785689715966, + "y": 285.63446752627516 + } + }, + { + "name": "1. Hello and welcome to Tdarr! This is a comment plugin. It doesn't do anything except help explain what's going on in a flow! You can place them anywhere and even link them together.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "df5cejfZP", + "position": { + "x": 774.8672137292031, + "y": -254.93856109034408 + } + }, + { + "name": "2. See! This comment won't do anything. The file from the previous plugin will be passed straight to the next plugin", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "30CajwYP2", + "position": { + "x": 644.6915712919753, + "y": 135.90533672888392 + } + }, + { + "name": "3. This here is an input file plugin and it's where every flow starts. You can only have ONE of these per flow.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "734dA76hg", + "position": { + "x": 444.5704060029551, + "y": -3.4693570957774114 + } + }, + { + "name": "4. That's it for this one, see you in the next chapter!", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "rkYonbPgX", + "position": { + "x": 443.9627448274695, + "y": 332.6480632642012 + } + } + ], + "flowEdges": [ + { + "source": "_YTuyCZg3", + "sourceHandle": "1", + "target": "30CajwYP2", + "targetHandle": null, + "id": "HUBIf10ny" + }, + { + "source": "30CajwYP2", + "sourceHandle": "1", + "target": "RQzydYbay", + "targetHandle": null, + "id": "Gd19X19w1" + }, + { + "source": "df5cejfZP", + "sourceHandle": "1", + "target": "30CajwYP2", + "targetHandle": null, + "id": "0EA92XgvP" + }, + { + "source": "734dA76hg", + "sourceHandle": "1", + "target": "rkYonbPgX", + "targetHandle": null, + "id": "lXbYouTsz" + } + ] +}); }; +exports.details = details; diff --git a/FlowPlugins/CommunityFlowTemplates/tutorials/chapter2.js b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter2.js new file mode 100644 index 000000000..fa3df8bfd --- /dev/null +++ b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter2.js @@ -0,0 +1,188 @@ +"use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; +var details = function () { return ({ + "name": "Chapter 2: The Basics", + "description": "Chapter 2: The Basics", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "p2KPpRjnB", + "position": { + "x": 414.1115477468154, + "y": -216.87055056329626 + } + }, + { + "name": "1. The flow follows the current 'working file' which we can run checks and take actions on.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "ecLynt2i0", + "position": { + "x": 197.4536903827362, + "y": -265.54506622009336 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "jUig7_cRU", + "position": { + "x": 439.02078192278447, + "y": 122.5624161723565 + } + }, + { + "name": "Rename File to have _BigFile", + "sourceRepo": "Community", + "pluginName": "renameFile", + "version": "1.0.0", + "inputsDB": { + "fileRename": "${fileName}_BigFile.${container}" + }, + "id": "2l0pB_oXW", + "position": { + "x": 257.94626475719076, + "y": -21.078426771503985 + } + }, + { + "name": "Check File Size", + "sourceRepo": "Community", + "pluginName": "checkFileSize", + "version": "1.0.0", + "inputsDB": { + "greaterThan": "1", + "lessThan": "10000" + }, + "id": "oDkceuMNL", + "position": { + "x": 413.7748155871969, + "y": -110.90469509295968 + } + }, + { + "name": "Each plugin can only have one input handle but many plugins can link to it. Plugins which only check something are typically orange coloured and have 2 or more outputs.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "HTvMe6FSV", + "position": { + "x": 34.402701566604065, + "y": -184.71873806260285 + } + }, + { + "name": "Once you make an action on a file, in almost all cases the output is the new file. It will be located in your library cache folder.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "3O3ECJdF-", + "position": { + "x": 33.1114649694174, + "y": 113.19141666640903 + } + }, + { + "name": "Typical usage is to replace the original file. So this plugin will replace the original file with the new file.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "qA8hT1mmP", + "position": { + "x": 355.2680532661178, + "y": 199.7482565776084 + } + }, + { + "name": "This flow route doesn't change the file, so the Replace Original File plugin won't do anything and the flow will end succesffully.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "1vBp13H02", + "position": { + "x": 597.7143477707415, + "y": -48.77347490679115 + } + }, + { + "name": "Double click on a plugin to see what each GREEN output does. Ignore the RED outputs for now.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "s-m8qOhJ7", + "position": { + "x": 34.4143205430116, + "y": -5.644569445757767 + } + } + ], + "flowEdges": [ + { + "source": "p2KPpRjnB", + "sourceHandle": "1", + "target": "oDkceuMNL", + "targetHandle": null, + "id": "S8inufSTF" + }, + { + "source": "oDkceuMNL", + "sourceHandle": "1", + "target": "2l0pB_oXW", + "targetHandle": null, + "id": "LFCRv0WUh" + }, + { + "source": "2l0pB_oXW", + "sourceHandle": "1", + "target": "jUig7_cRU", + "targetHandle": null, + "id": "w0K3dKylI" + }, + { + "source": "oDkceuMNL", + "sourceHandle": "2", + "target": "jUig7_cRU", + "targetHandle": null, + "id": "SNdz3urrJ" + }, + { + "source": "ecLynt2i0", + "sourceHandle": "1", + "target": "HTvMe6FSV", + "targetHandle": null, + "id": "7qPHR6V9P" + }, + { + "source": "3O3ECJdF-", + "sourceHandle": "1", + "target": "qA8hT1mmP", + "targetHandle": null, + "id": "GjDmOX_EI" + }, + { + "source": "HTvMe6FSV", + "sourceHandle": "1", + "target": "s-m8qOhJ7", + "targetHandle": null, + "id": "0bPlyyR9Q" + }, + { + "source": "s-m8qOhJ7", + "sourceHandle": "1", + "target": "3O3ECJdF-", + "targetHandle": null, + "id": "Mxxly19vC" + } + ] +}); }; +exports.details = details; diff --git a/FlowPlugins/CommunityFlowTemplates/tutorials/chapter3.js b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter3.js new file mode 100644 index 000000000..33241a63d --- /dev/null +++ b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter3.js @@ -0,0 +1,224 @@ +"use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; +var details = function () { return ({ + "name": "Chapter 3: FFmpeg Command", + "description": "Chapter 3: FFmpeg Command", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 648.9333795070321, + "y": -12.529435106431094 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "QdLvoNjuG", + "position": { + "x": 723.9430232247286, + "y": 534.7914903208923 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "-kY9osnGE", + "position": { + "x": 399.6705241883612, + "y": 143.02276817432977 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "pmoPx8W0W", + "position": { + "x": 400.42838247161643, + "y": 438.58749864385743 + } + }, + { + "name": "Set Container", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetContainer", + "version": "1.0.0", + "id": "-DEIJA3Pf", + "position": { + "x": 401.1862407548717, + "y": 335.51877212115033 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "U0fVPXskr", + "position": { + "x": 400.1862407548716, + "y": 249.12292783005773 + } + }, + { + "name": "The FFmpeg Command plugins dynamically create an FFmpeg command depending on the input file", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "hGnpEHnk5", + "position": { + "x": 254.91444207269103, + "y": -44.61887485112061 + } + }, + { + "name": "You must always begin an FFmpeg command using the 'Begin Command' Plugin", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "saj94a135", + "position": { + "x": 201.288800916537, + "y": 100.94856498487928 + } + }, + { + "name": "In this example, if the video file is already in h265/hevc and mkv container, no action will be taken on the file. To force re-encoding, you can use the forceEncoding option on the Video Encoder plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "sb5MvVryc", + "position": { + "x": 201.61485276007585, + "y": 222.09640730256172 + } + }, + { + "name": "Once the FFmpeg command has been created, you need to execute it using this plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "V0QGN5PKA", + "position": { + "x": 202.61485276007582, + "y": 440.0964073025617 + } + }, + { + "name": "Once again, the output contains the new cache file (or the original file if no action was taken on the file). If there's a new cache file, the 'Replace Original File' plugin will replace the original file, else it will do nothing.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "i4eODNlBc", + "position": { + "x": 536.6148527600759, + "y": 568.0964073025617 + } + } + ], + "flowEdges": [ + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "ldiZljXp2" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "-kY9osnGE", + "sourceHandle": "1", + "target": "U0fVPXskr", + "targetHandle": null, + "id": "wuqNLcC1D" + }, + { + "source": "U0fVPXskr", + "sourceHandle": "1", + "target": "-DEIJA3Pf", + "targetHandle": null, + "id": "Coq5pIs3c" + }, + { + "source": "-DEIJA3Pf", + "sourceHandle": "1", + "target": "pmoPx8W0W", + "targetHandle": null, + "id": "fGjbMXOng" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "-kY9osnGE", + "targetHandle": null, + "id": "E5NHstmdF" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "k9JywvYcK" + }, + { + "source": "hGnpEHnk5", + "sourceHandle": "1", + "target": "saj94a135", + "targetHandle": null, + "id": "dX6DiWPJX" + }, + { + "source": "saj94a135", + "sourceHandle": "1", + "target": "sb5MvVryc", + "targetHandle": null, + "id": "0MAqJvu_e" + }, + { + "source": "sb5MvVryc", + "sourceHandle": "1", + "target": "V0QGN5PKA", + "targetHandle": null, + "id": "57NrKKG2n" + }, + { + "source": "V0QGN5PKA", + "sourceHandle": "1", + "target": "i4eODNlBc", + "targetHandle": null, + "id": "BHwljK8rj" + } + ] +}); }; +exports.details = details; diff --git a/FlowPlugins/CommunityFlowTemplates/tutorials/chapter4p1.js b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter4p1.js new file mode 100644 index 000000000..5bf45db61 --- /dev/null +++ b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter4p1.js @@ -0,0 +1,213 @@ +"use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; +var details = function () { return ({ + "name": "Chapter 4: Flow Errors Part 1", + "description": "Chapter 4: Flow Errors Part 1", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 752.4065242952165, + "y": 51.12406033129332 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "QdLvoNjuG", + "position": { + "x": 773.1888091521793, + "y": 727.583503313465 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "-kY9osnGE", + "position": { + "x": 399.6705241883612, + "y": 143.02276817432977 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "pmoPx8W0W", + "position": { + "x": 416.1451226612283, + "y": 433.3485852473201 + } + }, + { + "name": "Set Container", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetContainer", + "version": "1.0.0", + "id": "-DEIJA3Pf", + "position": { + "x": 401.1862407548717, + "y": 335.51877212115033 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "U0fVPXskr", + "position": { + "x": 400.1862407548716, + "y": 249.12292783005773 + } + }, + { + "name": "If an unhandled error occurs during the flow, the flow will stop and the file will be moved to the Transcode: Error/Cancelled tab. You can then review the job report to see what went wrong.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "mNaOzfS0Y", + "position": { + "x": 604.5850500985517, + "y": 166.18413013606266 + } + }, + { + "name": "Fail Flow", + "sourceRepo": "Community", + "pluginName": "failFlow", + "version": "1.0.0", + "id": "mNwoZNlmo", + "position": { + "x": 616.8564543703576, + "y": 578.3209514237449 + } + }, + { + "name": "Compare File Size", + "sourceRepo": "Community", + "pluginName": "compareFileSize", + "version": "1.0.0", + "id": "YGd45fK8d", + "position": { + "x": 518.9335431151374, + "y": 502.8688871164036 + } + }, + { + "name": "You can also force a flow to fail which can be useful in certain situation such as here.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "9QkIvxxxx", + "position": { + "x": 678.3646507954192, + "y": 429.7476734555484 + } + } + ], + "flowEdges": [ + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "ldiZljXp2" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "-kY9osnGE", + "sourceHandle": "1", + "target": "U0fVPXskr", + "targetHandle": null, + "id": "wuqNLcC1D" + }, + { + "source": "U0fVPXskr", + "sourceHandle": "1", + "target": "-DEIJA3Pf", + "targetHandle": null, + "id": "Coq5pIs3c" + }, + { + "source": "-DEIJA3Pf", + "sourceHandle": "1", + "target": "pmoPx8W0W", + "targetHandle": null, + "id": "fGjbMXOng" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "-kY9osnGE", + "targetHandle": null, + "id": "E5NHstmdF" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "1", + "target": "YGd45fK8d", + "targetHandle": null, + "id": "bldP67hmm" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "fw9Le5zqo" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "2", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "wd7SmimpM" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "3", + "target": "mNwoZNlmo", + "targetHandle": null, + "id": "RmpqCf-Vh" + }, + { + "source": "mNaOzfS0Y", + "sourceHandle": "1", + "target": "9QkIvxxxx", + "targetHandle": null, + "id": "4Yez6rEN2" + } + ] +}); }; +exports.details = details; diff --git a/FlowPlugins/CommunityFlowTemplates/tutorials/chapter4p2.js b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter4p2.js new file mode 100644 index 000000000..ead074438 --- /dev/null +++ b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter4p2.js @@ -0,0 +1,307 @@ +"use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; +var details = function () { return ({ + "name": "Chapter 4: Flow Errors Part 2 - On Flow Error", + "description": "Chapter 4: Flow Errors Part 2 - On Flow Error", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 752.4065242952165, + "y": 51.12406033129332 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "QdLvoNjuG", + "position": { + "x": 773.1888091521793, + "y": 727.583503313465 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "-kY9osnGE", + "position": { + "x": 399.6705241883612, + "y": 143.02276817432977 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "pmoPx8W0W", + "position": { + "x": 416.1451226612283, + "y": 433.3485852473201 + } + }, + { + "name": "Set Container", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetContainer", + "version": "1.0.0", + "id": "-DEIJA3Pf", + "position": { + "x": 401.1862407548717, + "y": 335.51877212115033 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "U0fVPXskr", + "position": { + "x": 400.1862407548716, + "y": 249.12292783005773 + } + }, + { + "name": "Fail Flow", + "sourceRepo": "Community", + "pluginName": "failFlow", + "version": "1.0.0", + "id": "mNwoZNlmo", + "position": { + "x": 616.8564543703576, + "y": 578.3209514237449 + } + }, + { + "name": "Compare File Size", + "sourceRepo": "Community", + "pluginName": "compareFileSize", + "version": "1.0.0", + "id": "YGd45fK8d", + "position": { + "x": 518.9335431151374, + "y": 502.8688871164036 + } + }, + { + "name": "On Flow Error", + "sourceRepo": "Community", + "pluginName": "onFlowError", + "version": "1.0.0", + "id": "yMWso-uZa", + "position": { + "x": 922.4197900595414, + "y": 161.088098623682 + } + }, + { + "name": "1. To handle an error that occurs anywhere in this specifc flow, you can use the 'On Flow Error' plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "jpEn9FVQX", + "position": { + "x": 1018.5785464566798, + "y": 65.98583847747655 + } + }, + { + "name": "All unhandled errors and the 'Fail Flow' plugin IN THIS FLOW will trigger the 'On Flow Error' plugin IN THIS FLOW", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "vRFTPo0p5", + "position": { + "x": 698.3866844766682, + "y": 451.2472106052397 + } + }, + { + "name": "If another error occurs in the 'On Flow Error' flow then the flow will end and the file will be moved to the transcode 'Transcode: Error/Cancelled' tab. The 'On Flow Error' plugin will NOT be run again (to prevent infinite error loops)", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "fAQouEkEY", + "position": { + "x": 1183.421069816697, + "y": 228.19157008625297 + } + }, + { + "name": "Send Web Request", + "sourceRepo": "Community", + "pluginName": "webRequest", + "version": "1.0.0", + "id": "42P9lb0B3", + "position": { + "x": 897.7260729664589, + "y": 469.1243455181426 + } + }, + { + "name": "Even if all the plugins in the error flow complete successfully, the file will still be moved to the 'Transcode: Error/Cancelled' tab at the end.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "Ke1S57we6", + "position": { + "x": 1006.3188415680227, + "y": 541.3784972055968 + } + }, + { + "name": "Fail Flow", + "sourceRepo": "Community", + "pluginName": "failFlow", + "version": "1.0.0", + "id": "yj3grm5d8", + "position": { + "x": 1047.7090907308577, + "y": 394.7331214427515 + } + }, + { + "name": "Check File Exists", + "sourceRepo": "Community", + "pluginName": "checkFileExists", + "version": "1.0.0", + "id": "S_rVuKn8S", + "position": { + "x": 926.2665476107438, + "y": 277.7066707997331 + } + } + ], + "flowEdges": [ + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "ldiZljXp2" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "-kY9osnGE", + "sourceHandle": "1", + "target": "U0fVPXskr", + "targetHandle": null, + "id": "wuqNLcC1D" + }, + { + "source": "U0fVPXskr", + "sourceHandle": "1", + "target": "-DEIJA3Pf", + "targetHandle": null, + "id": "Coq5pIs3c" + }, + { + "source": "-DEIJA3Pf", + "sourceHandle": "1", + "target": "pmoPx8W0W", + "targetHandle": null, + "id": "fGjbMXOng" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "-kY9osnGE", + "targetHandle": null, + "id": "E5NHstmdF" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "1", + "target": "YGd45fK8d", + "targetHandle": null, + "id": "bldP67hmm" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "fw9Le5zqo" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "2", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "wd7SmimpM" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "3", + "target": "mNwoZNlmo", + "targetHandle": null, + "id": "RmpqCf-Vh" + }, + { + "source": "jpEn9FVQX", + "sourceHandle": "1", + "target": "fAQouEkEY", + "targetHandle": null, + "id": "5mv1ls7Ib" + }, + { + "source": "fAQouEkEY", + "sourceHandle": "1", + "target": "Ke1S57we6", + "targetHandle": null, + "id": "_VEvhMOtk" + }, + { + "source": "yMWso-uZa", + "sourceHandle": "1", + "target": "S_rVuKn8S", + "targetHandle": null, + "id": "yweCdlSWM" + }, + { + "source": "S_rVuKn8S", + "sourceHandle": "1", + "target": "yj3grm5d8", + "targetHandle": null, + "id": "xI3eh7wZp" + }, + { + "source": "S_rVuKn8S", + "sourceHandle": "2", + "target": "42P9lb0B3", + "targetHandle": null, + "id": "V-qf6QBC4" + } + ] +}); }; +exports.details = details; diff --git a/FlowPlugins/CommunityFlowTemplates/tutorials/chapter4p3.js b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter4p3.js new file mode 100644 index 000000000..e04a57ba2 --- /dev/null +++ b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter4p3.js @@ -0,0 +1,314 @@ +"use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; +var details = function () { return ({ + "name": "Chapter 4: Flow Errors Part 3 - Plugin-specific Error Handling", + "description": "Chapter 4: Flow Errors Part 3 - Plugin-specific Error Handling", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 541.9238836009351, + "y": 32.863009312154745 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "QdLvoNjuG", + "position": { + "x": 773.1888091521793, + "y": 727.583503313465 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "-kY9osnGE", + "position": { + "x": 399.6705241883612, + "y": 143.02276817432977 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "pmoPx8W0W", + "position": { + "x": 399.8062875388412, + "y": 417.9708580733087 + } + }, + { + "name": "Set Container", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetContainer", + "version": "1.0.0", + "id": "-DEIJA3Pf", + "position": { + "x": 401.1862407548717, + "y": 335.51877212115033 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "U0fVPXskr", + "position": { + "x": 400.1862407548716, + "y": 249.12292783005773 + } + }, + { + "name": "Fail Flow", + "sourceRepo": "Community", + "pluginName": "failFlow", + "version": "1.0.0", + "id": "mNwoZNlmo", + "position": { + "x": 616.8564543703576, + "y": 578.3209514237449 + } + }, + { + "name": "Compare File Size", + "sourceRepo": "Community", + "pluginName": "compareFileSize", + "version": "1.0.0", + "id": "YGd45fK8d", + "position": { + "x": 518.9335431151374, + "y": 502.8688871164036 + } + }, + { + "name": "On Flow Error", + "sourceRepo": "Community", + "pluginName": "onFlowError", + "version": "1.0.0", + "id": "yMWso-uZa", + "position": { + "x": 1060.1712089610803, + "y": 145.28055874973487 + } + }, + { + "name": "Send Web Request", + "sourceRepo": "Community", + "pluginName": "webRequest", + "version": "1.0.0", + "id": "Bc2bZtgBc", + "position": { + "x": 922.4677516191077, + "y": 279.351803016885 + } + }, + { + "name": "Tdarr also offers plugin-specific error handling using the RED connection on each plugin. The flow path will be triggered if an unhandled error occurs within that specific plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "H__edbTLw", + "position": { + "x": 767.7320724785685, + "y": -230.81328720265796 + } + }, + { + "name": "Send Web Request: Ping Melissa to check network storage", + "sourceRepo": "Community", + "pluginName": "webRequest", + "version": "1.0.0", + "id": "X9NEJCEgk", + "position": { + "x": 880.7836232681229, + "y": -39.676791653194755 + } + }, + { + "name": "Send Web Request: Ping Romesh to check transcode log", + "sourceRepo": "Community", + "pluginName": "webRequest", + "version": "1.0.0", + "id": "BO0c5TlKq", + "position": { + "x": 661.6510110384603, + "y": 300.5554220718082 + } + }, + { + "name": "This allows very specific error flows, for example pinging different team members for different errors.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "zTVwtbuxI", + "position": { + "x": 657.8440331872728, + "y": 166.67570418496692 + } + }, + { + "name": "The plugin-specifc error handling will NOT trigger the 'On Flow Error' plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "N9E-u8l0o", + "position": { + "x": 1170.7519669401188, + "y": 56.50523025141092 + } + }, + { + "name": "But you can still join the plugin-specific error handling flow onto the rest of the 'On Flow Error' Flow", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "AOi3vLobO", + "position": { + "x": 1125.9768689675689, + "y": 284.4868129833375 + } + } + ], + "flowEdges": [ + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "ldiZljXp2" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "-kY9osnGE", + "sourceHandle": "1", + "target": "U0fVPXskr", + "targetHandle": null, + "id": "wuqNLcC1D" + }, + { + "source": "U0fVPXskr", + "sourceHandle": "1", + "target": "-DEIJA3Pf", + "targetHandle": null, + "id": "Coq5pIs3c" + }, + { + "source": "-DEIJA3Pf", + "sourceHandle": "1", + "target": "pmoPx8W0W", + "targetHandle": null, + "id": "fGjbMXOng" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "-kY9osnGE", + "targetHandle": null, + "id": "E5NHstmdF" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "1", + "target": "YGd45fK8d", + "targetHandle": null, + "id": "bldP67hmm" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "fw9Le5zqo" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "2", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "wd7SmimpM" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "3", + "target": "mNwoZNlmo", + "targetHandle": null, + "id": "RmpqCf-Vh" + }, + { + "source": "yMWso-uZa", + "sourceHandle": "1", + "target": "Bc2bZtgBc", + "targetHandle": null, + "id": "7k8P1VYv6" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "err1", + "target": "X9NEJCEgk", + "targetHandle": null, + "id": "9rhuR5eSI" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "err1", + "target": "BO0c5TlKq", + "targetHandle": null, + "id": "ttZgLtKF3" + }, + { + "source": "H__edbTLw", + "sourceHandle": "1", + "target": "zTVwtbuxI", + "targetHandle": null, + "id": "5sjNNMXAK" + }, + { + "source": "X9NEJCEgk", + "sourceHandle": "1", + "target": "Bc2bZtgBc", + "targetHandle": null, + "id": "OW-yqRQH5" + }, + { + "source": "N9E-u8l0o", + "sourceHandle": "1", + "target": "AOi3vLobO", + "targetHandle": null, + "id": "K440_LQm_" + } + ] +}); }; +exports.details = details; diff --git a/FlowPlugins/CommunityFlowTemplates/tutorials/chapter5.js b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter5.js new file mode 100644 index 000000000..27fc615d9 --- /dev/null +++ b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter5.js @@ -0,0 +1,156 @@ +"use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; +var details = function () { return ({ + "name": "Chapter 5: Go To Flow", + "description": "Chapter 5: Go To Flow", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 752.4065242952165, + "y": 51.12406033129332 + } + }, + { + "name": "On Flow Error", + "sourceRepo": "Community", + "pluginName": "onFlowError", + "version": "1.0.0", + "id": "yMWso-uZa", + "position": { + "x": 1122.33024332169, + "y": 226.4434391132305 + } + }, + { + "name": "You can use the Go To Flow to go to a different flow. The working file will be passed to that flow and will continue as normal. Double click on the plugin to select the flow you'd like to go to.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "rHV28Kbkv", + "position": { + "x": 462.0014512264263, + "y": 65.78412788449464 + } + }, + { + "name": "Go To Flow", + "sourceRepo": "Community", + "pluginName": "goToFlow", + "version": "1.0.0", + "id": "gOrbropah", + "position": { + "x": 572.7308895655424, + "y": 234.58707695358294 + } + }, + { + "name": "By design, if an error happens in a different flow, this 'On Flow Error' will not be called. Across all flows, the 'On Flow Error' plugin will only be called ONCE in the flow that the FIRST error occurred in. 'On Flow Error' plugins in flows before or after the current flow will not be called, even if an error occurs in them at a later time.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "7azuiVML9", + "position": { + "x": 1310.2464269220861, + "y": 134.70796523124582 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "ELn0kcc-1", + "position": { + "x": 778.4079905179452, + "y": 428.7308825254772 + } + }, + { + "name": "Go To Flow", + "sourceRepo": "Community", + "pluginName": "goToFlow", + "version": "1.0.0", + "id": "j5dOGi9zz", + "position": { + "x": 1122.484636036451, + "y": 397.97542817745443 + } + }, + { + "name": "After an error has occured you can even go to a different flow! So you can create a dedicated Error flow and go to it each time an error occurs within any of your flows! Useful for notifications etc.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "EpMxr2UuE", + "position": { + "x": 1205.7688307851763, + "y": 479.4625484620842 + } + } + ], + "flowEdges": [ + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "gOrbropah", + "targetHandle": null, + "id": "qVWE7SWt2" + }, + { + "source": "rHV28Kbkv", + "sourceHandle": "1", + "target": "7azuiVML9", + "targetHandle": null, + "id": "i0OUf3hAM" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "ELn0kcc-1", + "targetHandle": null, + "id": "hTaDcPw24" + }, + { + "source": "yMWso-uZa", + "sourceHandle": "1", + "target": "j5dOGi9zz", + "targetHandle": null, + "id": "6Hrh7vbfW" + }, + { + "source": "7azuiVML9", + "sourceHandle": "1", + "target": "EpMxr2UuE", + "targetHandle": null, + "id": "S_36mCXnL" + } + ] +}); }; +exports.details = details; diff --git a/FlowPlugins/CommunityFlowTemplates/tutorials/chapter6.js b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter6.js new file mode 100644 index 000000000..aedc2a24d --- /dev/null +++ b/FlowPlugins/CommunityFlowTemplates/tutorials/chapter6.js @@ -0,0 +1,267 @@ +"use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; +var details = function () { return ({ + "name": "Chapter 6: The Review System", + "description": "Chapter 6: The Review System", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 752.4065242952165, + "y": 51.12406033129332 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "R0gX9B20d", + "position": { + "x": 879.7236115475249, + "y": 934.782797377857 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "U6N3AQubH", + "position": { + "x": 546.8854528742303, + "y": 174.54090453410515 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "Lv-zb-iTw", + "position": { + "x": 543.172691292081, + "y": 368.6158072160807 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "1pOFUCuQR", + "position": { + "x": 545.0642491154337, + "y": 274.70711126791645 + } + }, + { + "name": "Require Review", + "sourceRepo": "Community", + "pluginName": "requireReview", + "version": "1.0.0", + "id": "oHpu2fZOi", + "position": { + "x": 631.7959812272709, + "y": 459.542392214296 + } + }, + { + "name": "Run Classic Transcode Plugin: Add Audio Stream", + "sourceRepo": "Community", + "pluginName": "runClassicTranscodePlugin", + "version": "1.0.0", + "inputsDB": { + "pluginSourceId": "Community:Tdarr_Plugin_00td_action_add_audio_stream_codec" + }, + "id": "RZX5jIP5I", + "position": { + "x": 632.2402074212371, + "y": 545.8356807635909 + } + }, + { + "name": "Run Classic Transcode Plugin", + "sourceRepo": "Community", + "pluginName": "runClassicTranscodePlugin", + "version": "1.0.0", + "inputsDB": { + "pluginSourceId": "Community:Tdarr_Plugin_00td_action_remove_audio_by_channel_count", + "channelCounts": "8" + }, + "id": "3zj5puRQ1", + "position": { + "x": 696.5390735282473, + "y": 758.7339551203235 + } + }, + { + "name": "Require Review", + "sourceRepo": "Community", + "pluginName": "requireReview", + "version": "1.0.0", + "id": "q8Pz_3HGh", + "position": { + "x": 634.7388490591334, + "y": 648.8668893974542 + } + }, + { + "name": "You can pause a flow by using the 'Require Review' plugin. This will cause the file to stay in the staging section on the Tdarr tab until the 'Reviewed' button is pressed. This allows you to check the last completed cache file.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "YPyMAbZ76", + "position": { + "x": 856.2716462414401, + "y": 343.06736953610425 + } + }, + { + "name": "Once the file has been reviewed, the flow will continue from the next plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "REt4UEEGD", + "position": { + "x": 856.7260936183322, + "y": 531.8099095812979 + } + }, + { + "name": "You can 'Require Review' as much as you like!", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "UhuSLjA8g", + "position": { + "x": 857.7070495622867, + "y": 709.362935437006 + } + }, + { + "name": "Require Review", + "sourceRepo": "Community", + "pluginName": "requireReview", + "version": "1.0.0", + "id": "8IU0bhEJs", + "position": { + "x": 780.2115299899054, + "y": 844.7348557026837 + } + } + ], + "flowEdges": [ + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "U6N3AQubH", + "sourceHandle": "1", + "target": "1pOFUCuQR", + "targetHandle": null, + "id": "RdnvWmv0o" + }, + { + "source": "1pOFUCuQR", + "sourceHandle": "1", + "target": "Lv-zb-iTw", + "targetHandle": null, + "id": "p-VkIS6DK" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "U6N3AQubH", + "targetHandle": null, + "id": "x_vWzShYB" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "R0gX9B20d", + "targetHandle": null, + "id": "CtIsUppTB" + }, + { + "source": "Lv-zb-iTw", + "sourceHandle": "1", + "target": "oHpu2fZOi", + "targetHandle": null, + "id": "d9tDIjd1L" + }, + { + "source": "oHpu2fZOi", + "sourceHandle": "1", + "target": "RZX5jIP5I", + "targetHandle": null, + "id": "kFP4WRftx" + }, + { + "source": "RZX5jIP5I", + "sourceHandle": "1", + "target": "q8Pz_3HGh", + "targetHandle": null, + "id": "nqbQJ9wUz" + }, + { + "source": "q8Pz_3HGh", + "sourceHandle": "1", + "target": "3zj5puRQ1", + "targetHandle": null, + "id": "Vx60urLP7" + }, + { + "source": "YPyMAbZ76", + "sourceHandle": "1", + "target": "REt4UEEGD", + "targetHandle": null, + "id": "B85VWeRRu" + }, + { + "source": "REt4UEEGD", + "sourceHandle": "1", + "target": "UhuSLjA8g", + "targetHandle": null, + "id": "wk44u1THD" + }, + { + "source": "3zj5puRQ1", + "sourceHandle": "1", + "target": "8IU0bhEJs", + "targetHandle": null, + "id": "9Q4vfDFmI" + }, + { + "source": "8IU0bhEJs", + "sourceHandle": "1", + "target": "R0gX9B20d", + "targetHandle": null, + "id": "8wXVHwiDC" + } + ] +}); }; +exports.details = details; diff --git a/FlowPlugins/CommunityFlowTemplates/video/basicVideo.js b/FlowPlugins/CommunityFlowTemplates/video/basicVideo.js index 26ff4e0b0..3a5f94c14 100644 --- a/FlowPlugins/CommunityFlowTemplates/video/basicVideo.js +++ b/FlowPlugins/CommunityFlowTemplates/video/basicVideo.js @@ -1,8 +1,12 @@ "use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; var details = function () { return ({ name: 'Basic HEVC Video Flow', description: 'Basic HEVC Video Flow', - tags: 'video', + tags: '', flowPlugins: [ { name: 'Input File', @@ -89,8 +93,6 @@ var details = function () { return ({ target: '91b7IrsEc', targetHandle: null, id: 'HhF4rw2DZ', - animated: true, - type: 'smoothstep', }, { source: '91b7IrsEc', @@ -98,8 +100,6 @@ var details = function () { return ({ target: '4Swd6qzvc', targetHandle: null, id: 'jJizyFUcr', - animated: true, - type: 'smoothstep', }, { source: '4Swd6qzvc', @@ -107,8 +107,6 @@ var details = function () { return ({ target: '8B_6pRd_U', targetHandle: null, id: '3Df7Xoy93', - animated: true, - type: 'smoothstep', }, { source: '450g167D8', @@ -116,8 +114,6 @@ var details = function () { return ({ target: '4fkfOyR3l', targetHandle: null, id: 'rE5Dsh9KM', - animated: true, - type: 'smoothstep', }, { source: '91b7IrsEc', @@ -125,8 +121,6 @@ var details = function () { return ({ target: '4fkfOyR3l', targetHandle: null, id: 'W2nVG7ts5', - animated: true, - type: 'smoothstep', }, { source: '8B_6pRd_U', @@ -134,8 +128,6 @@ var details = function () { return ({ target: 'TtKXi3Q7h', targetHandle: null, id: 'epqtLsPuG', - animated: true, - type: 'smoothstep', }, { source: 'TtKXi3Q7h', @@ -143,9 +135,7 @@ var details = function () { return ({ target: '450g167D8', targetHandle: null, id: 'ljOeP0cAZ', - animated: true, - type: 'smoothstep', }, ], }); }; -module.exports.details = details; +exports.details = details; diff --git a/FlowPlugins/CommunityFlowTemplates/video/lowResolutionCopies.js b/FlowPlugins/CommunityFlowTemplates/video/lowResolutionCopies.js new file mode 100644 index 000000000..a1fef3cc3 --- /dev/null +++ b/FlowPlugins/CommunityFlowTemplates/video/lowResolutionCopies.js @@ -0,0 +1,362 @@ +"use strict"; +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.details = void 0; +var details = function () { return ({ + name: 'Create Low Resolution Video Copies', + description: 'Create Low Resolution Video Copies', + tags: '', + flowPlugins: [ + { + name: 'Input File', + sourceRepo: 'Community', + pluginName: 'inputFile', + version: '1.0.0', + id: 'pE6rU7gkW', + position: { + x: 764.3859715446088, + y: 54.59674430707997, + }, + }, + { + name: 'Check File Exists _480p', + sourceRepo: 'Community', + pluginName: 'checkFileExists', + version: '1.0.0', + inputsDB: { + fileToCheck: '${fileName}_480p.${container}', + }, + id: 'VyNRD3YjM', + position: { + x: 1127.8807371830678, + y: -1.4370146635981769, + }, + }, + { + name: 'Rename File _480p', + sourceRepo: 'Community', + pluginName: 'renameFile', + version: '1.0.0', + inputsDB: { + fileRename: '${fileName}_480p.${container}', + }, + id: 'VpCD-7LZJ', + position: { + x: 1398.163993949301, + y: 562.3533349776774, + }, + }, + { + name: 'Replace Original File', + sourceRepo: 'Community', + pluginName: 'replaceOriginalFile', + version: '1.0.0', + id: '1pj9oSg5G', + position: { + x: 736.3406162570204, + y: 598.8673432638388, + }, + }, + { + name: 'Check File Exists _720p', + sourceRepo: 'Community', + pluginName: 'checkFileExists', + version: '1.0.0', + inputsDB: { + fileToCheck: '${fileName}_720p.${container}', + }, + id: 'uDC6XT1Jy', + position: { + x: 1060.0100333142968, + y: 110.8981370311281, + }, + }, + { + name: 'Check File Name Includes', + sourceRepo: 'Community', + pluginName: 'checkFileNameIncludes', + version: '1.0.0', + inputsDB: { + terms: '_720p,_480p', + }, + id: 'wRipuaq4G', + position: { + x: 763.9976994431687, + y: 198.97576654117708, + }, + }, + { + name: 'Rename File _720p', + sourceRepo: 'Community', + pluginName: 'renameFile', + version: '1.0.0', + inputsDB: { + fileRename: '${fileName}_720p.${container}', + }, + id: 'cTKbaB8nT', + position: { + x: 1087.883110780845, + y: 509.6274273776863, + }, + }, + { + name: 'Move To Original Directory', + sourceRepo: 'Community', + pluginName: 'moveToOriginalDirectory', + version: '1.0.0', + id: 'mFRK-Z9WC', + position: { + x: 1162.0438576735944, + y: 608.0524996697887, + }, + }, + { + name: 'Set Original File', + sourceRepo: 'Community', + pluginName: 'setOriginalFile', + version: '1.0.0', + id: 'oD4u5PY9T', + position: { + x: 1020.6746394849897, + y: 738.9349550904227, + }, + }, + { + name: 'Begin Command', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandStart', + version: '1.0.0', + id: 'FSG9AOX5c', + position: { + x: 1171.2902386661297, + y: 178.0193821518036, + }, + }, + { + name: 'Set Video Encoder', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVideoEncoder', + version: '1.0.0', + inputsDB: { + forceEncoding: 'true', + }, + id: 'wcmBN2N02', + position: { + x: 1171.0819612214827, + y: 257.19366435734827, + }, + }, + { + name: 'Execute', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandExecute', + version: '1.0.0', + id: 'tmUd79-Fb', + position: { + x: 1167.5698309351776, + y: 406.2043896501846, + }, + }, + { + name: 'Begin Command', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandStart', + version: '1.0.0', + id: 'Jn6dcKd3i', + position: { + x: 1395.4614497255334, + y: 111.74717420966138, + }, + }, + { + name: 'Execute', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandExecute', + version: '1.0.0', + id: 'gbY0xIJnB', + position: { + x: 1398.0103706416776, + y: 417.6787803779547, + }, + }, + { + name: 'Set Video Resolution 480p', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVdeoResolution', + version: '1.0.0', + inputsDB: { + targetResolution: '480p', + }, + id: 'dzFEwECXB', + position: { + x: 1396.1961096759603, + y: 309.9727302535869, + }, + }, + { + name: 'Set Video Encoder', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVideoEncoder', + version: '1.0.0', + inputsDB: { + forceEncoding: 'true', + }, + id: '_EynbvgSl', + position: { + x: 1396.1961096759603, + y: 214.35898180146438, + }, + }, + { + name: 'Set Video Resolution 720p', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVdeoResolution', + version: '1.0.0', + inputsDB: { + targetResolution: '720p', + }, + id: 'CMm7MlE7g', + position: { + x: 1169.6624226114702, + y: 336.82482287402803, + }, + }, + ], + flowEdges: [ + { + source: 'pE6rU7gkW', + sourceHandle: '1', + target: 'wRipuaq4G', + targetHandle: null, + id: 'IE_oGhETB', + }, + { + source: 'wRipuaq4G', + sourceHandle: '1', + target: '1pj9oSg5G', + targetHandle: null, + id: 'QR6uGNUhE', + }, + { + source: 'wRipuaq4G', + sourceHandle: '2', + target: 'VyNRD3YjM', + targetHandle: null, + id: 'sh_kstv0D', + }, + { + source: 'uDC6XT1Jy', + sourceHandle: '1', + target: '1pj9oSg5G', + targetHandle: null, + id: 'G5jl85ijr', + }, + { + source: 'VyNRD3YjM', + sourceHandle: '1', + target: 'uDC6XT1Jy', + targetHandle: null, + id: 'DmUL9DS8q', + }, + { + source: 'VpCD-7LZJ', + sourceHandle: '1', + target: 'mFRK-Z9WC', + targetHandle: null, + id: 'ap4YXAxy3', + }, + { + source: 'cTKbaB8nT', + sourceHandle: '1', + target: 'mFRK-Z9WC', + targetHandle: null, + id: 'i9fr5J5pL', + }, + { + source: 'mFRK-Z9WC', + sourceHandle: '1', + target: 'oD4u5PY9T', + targetHandle: null, + id: 'KUw59S_Zl', + }, + { + source: 'oD4u5PY9T', + sourceHandle: '1', + target: 'wRipuaq4G', + targetHandle: null, + id: 'HlM4E6eV8', + }, + { + source: 'tmUd79-Fb', + sourceHandle: '1', + target: 'cTKbaB8nT', + targetHandle: null, + id: 'iJLmmoDLp', + }, + { + source: 'uDC6XT1Jy', + sourceHandle: '2', + target: 'FSG9AOX5c', + targetHandle: null, + id: 'iRTrU8utq', + }, + { + source: 'dzFEwECXB', + sourceHandle: '1', + target: 'gbY0xIJnB', + targetHandle: null, + id: 'A5cyCu_kx', + }, + { + source: 'Jn6dcKd3i', + sourceHandle: '1', + target: '_EynbvgSl', + targetHandle: null, + id: '1HajidLz-', + }, + { + source: '_EynbvgSl', + sourceHandle: '1', + target: 'dzFEwECXB', + targetHandle: null, + id: 'vEESYeSsL', + }, + { + source: 'VyNRD3YjM', + sourceHandle: '2', + target: 'Jn6dcKd3i', + targetHandle: null, + id: 'q8zd_qCSU', + }, + { + source: 'gbY0xIJnB', + sourceHandle: '1', + target: 'VpCD-7LZJ', + targetHandle: null, + id: 'leYMQdxHw', + }, + { + source: 'FSG9AOX5c', + sourceHandle: '1', + target: 'wcmBN2N02', + targetHandle: null, + id: 'Dl5MCSqQM', + }, + { + source: 'wcmBN2N02', + sourceHandle: '1', + target: 'CMm7MlE7g', + targetHandle: null, + id: 'GIpbjomC8', + }, + { + source: 'CMm7MlE7g', + sourceHandle: '1', + target: 'tmUd79-Fb', + targetHandle: null, + id: 'AxR9R10MY', + }, + ], +}); }; +exports.details = details; diff --git a/FlowPlugins/FlowHelpers/1.0.0/classicPlugins.js b/FlowPlugins/FlowHelpers/1.0.0/classicPlugins.js new file mode 100644 index 000000000..f37b54528 --- /dev/null +++ b/FlowPlugins/FlowHelpers/1.0.0/classicPlugins.js @@ -0,0 +1,155 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.runClassicPlugin = void 0; +var fs_1 = require("fs"); +var fileUtils_1 = require("./fileUtils"); +var runClassicPlugin = function (args, type) { return __awaiter(void 0, void 0, void 0, function () { + var path, pluginSourceId, parts, pluginSource, pluginId, relativePluginPath, absolutePath, classicPlugin, pluginSrcStr, res, container, cacheFilePath, scanTypes, pluginInputFileObj, originalLibraryFile, otherArguments, result; + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + path = require('path'); + pluginSourceId = String(args.inputs.pluginSourceId); + parts = pluginSourceId.split(':'); + pluginSource = parts[0]; + pluginId = parts[1]; + relativePluginPath = "../../../".concat(pluginSource, "/").concat(pluginId, ".js"); + absolutePath = path.resolve(__dirname, relativePluginPath); + pluginSrcStr = ''; + if (!(pluginSource === 'Community')) return [3 /*break*/, 2]; + classicPlugin = args.deps.importFresh(relativePluginPath); + return [4 /*yield*/, fs_1.promises.readFile(absolutePath, 'utf8')]; + case 1: + pluginSrcStr = _b.sent(); + return [3 /*break*/, 4]; + case 2: return [4 /*yield*/, args.deps.axiosMiddleware('api/v2/read-plugin', { + plugin: { + id: pluginId, + source: pluginSource, + }, + })]; + case 3: + res = _b.sent(); + classicPlugin = args.deps.requireFromString(res.pluginRaw, absolutePath); + pluginSrcStr = res.pluginRaw; + _b.label = 4; + case 4: + if (type === 'filter' && classicPlugin.details().Operation !== 'Filter') { + throw new Error("".concat('This plugin is meant for classic plugins that have ' + + 'Operation: Filter. This classic plugin has Operation: ').concat(classicPlugin.details().Operation) + + '. Please use the Run Classic Transcode Flow Plugin plugin instead.'); + } + if (type !== 'filter' && classicPlugin.details().Operation === 'Filter') { + throw new Error("".concat('This plugin is meant for classic plugins that have ' + + 'Operation: Transcode. This classic plugin has Operation: ').concat(classicPlugin.details().Operation) + + 'Please use the Run Classic Filter Flow Plugin plugin instead.'); + } + if (!Array.isArray(classicPlugin.dependencies)) return [3 /*break*/, 8]; + if (!args.installClassicPluginDeps) return [3 /*break*/, 6]; + args.jobLog("Installing dependencies for ".concat(pluginSourceId)); + return [4 /*yield*/, args.installClassicPluginDeps(classicPlugin.dependencies)]; + case 5: + _b.sent(); + return [3 /*break*/, 7]; + case 6: + args.jobLog("Not installing dependencies for ".concat(pluginSourceId, ", please update Tdarr")); + _b.label = 7; + case 7: return [3 /*break*/, 9]; + case 8: + args.jobLog("No depedencies to install for ".concat(pluginSourceId)); + _b.label = 9; + case 9: + container = (0, fileUtils_1.getContainer)(args.inputFileObj._id); + cacheFilePath = "".concat((0, fileUtils_1.getPluginWorkDir)(args), "/").concat((0, fileUtils_1.getFileName)(args.inputFileObj._id), ".").concat(container); + scanTypes = (0, fileUtils_1.getScanTypes)([pluginSrcStr]); + return [4 /*yield*/, args.deps.axiosMiddleware('api/v2/scan-individual-file', { + file: { + _id: args.inputFileObj._id, + file: args.inputFileObj.file, + DB: args.inputFileObj.DB, + footprintId: args.inputFileObj.footprintId, + }, + scanTypes: scanTypes, + })]; + case 10: + pluginInputFileObj = _b.sent(); + return [4 /*yield*/, args.deps.axiosMiddleware('api/v2/scan-individual-file', { + file: { + _id: args.originalLibraryFile._id, + file: args.originalLibraryFile.file, + DB: args.originalLibraryFile.DB, + footprintId: args.originalLibraryFile.footprintId, + }, + scanTypes: scanTypes, + })]; + case 11: + originalLibraryFile = _b.sent(); + otherArguments = { + handbrakePath: args.handbrakePath, + ffmpegPath: args.ffmpegPath, + mkvpropeditPath: args.mkvpropeditPath, + originalLibraryFile: originalLibraryFile, + nodeHardwareType: args.nodeHardwareType, + pluginCycle: 0, + workerType: args.workerType, + version: args.config.version, + platform_arch_isdocker: args.platform_arch_isdocker, + cacheFilePath: cacheFilePath, + job: args.job, + }; + return [4 /*yield*/, classicPlugin.plugin(pluginInputFileObj, args.librarySettings, args.inputs, otherArguments)]; + case 12: + result = _b.sent(); + if (((_a = result === null || result === void 0 ? void 0 : result.file) === null || _a === void 0 ? void 0 : _a._id) && args.inputFileObj._id !== result.file._id) { + // eslint-disable-next-line no-param-reassign + args.inputFileObj._id = result.file._id; + // eslint-disable-next-line no-param-reassign + args.inputFileObj.file = result.file.file; + args.jobLog("File ID changed from ".concat(args.inputFileObj._id, " to ").concat(result.file._id)); + } + return [2 /*return*/, { + result: result, + cacheFilePath: cacheFilePath, + absolutePath: absolutePath, + }]; + } + }); +}); }; +exports.runClassicPlugin = runClassicPlugin; diff --git a/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js b/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js index 8f1a349dc..5980e9aa6 100644 --- a/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js +++ b/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js @@ -36,7 +36,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { } }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.moveFileAndValidate = exports.getSubStem = exports.getFfType = exports.getFileName = exports.getContainer = void 0; +exports.getScanTypes = exports.getPluginWorkDir = exports.moveFileAndValidate = exports.getSubStem = exports.getFfType = exports.getFileAbosluteDir = exports.getFileName = exports.getContainer = void 0; var fs_1 = require("fs"); var getContainer = function (filePath) { var parts = filePath.split('.'); @@ -51,6 +51,12 @@ var getFileName = function (filePath) { return parts2.join('.'); }; exports.getFileName = getFileName; +var getFileAbosluteDir = function (filePath) { + var parts = filePath.split('/'); + parts.pop(); + return parts.join('/'); +}; +exports.getFileAbosluteDir = getFileAbosluteDir; var getFfType = function (codecType) { return (codecType === 'video' ? 'v' : 'a'); }; exports.getFfType = getFfType; var getSubStem = function (_a) { @@ -140,3 +146,47 @@ var moveFileAndValidate = function (_a) { }); }; exports.moveFileAndValidate = moveFileAndValidate; +var getPluginWorkDir = function (args) { + var pluginWorkDir = "".concat(args.workDir, "/").concat(new Date().getTime()); + args.deps.fsextra.ensureDirSync(pluginWorkDir); + return pluginWorkDir; +}; +exports.getPluginWorkDir = getPluginWorkDir; +var getScanTypes = function (pluginsTextRaw) { + var scanTypes = { + exifToolScan: true, + mediaInfoScan: false, + closedCaptionScan: false, + }; + var scannerTypes = [ + // needed for frame and duration data for ffmpeg + // { + // type: 'exifToolScan', + // terms: [ + // 'meta', + // ], + // }, + { + type: 'mediaInfoScan', + terms: [ + 'mediaInfo', + ], + }, + { + type: 'closedCaptionScan', + terms: [ + 'hasClosedCaptions', + ], + }, + ]; + var text = pluginsTextRaw.join(''); + scannerTypes.forEach(function (scanner) { + scanner.terms.forEach(function (term) { + if (text.includes(term)) { + scanTypes[scanner.type] = true; + } + }); + }); + return scanTypes; +}; +exports.getScanTypes = getScanTypes; diff --git a/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.js b/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.js index 44fad7bb4..fe39337c8 100644 --- a/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.js +++ b/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.js @@ -49,7 +49,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { Object.defineProperty(exports, "__esModule", { value: true }); exports.getEncoder = exports.getBestNvencDevice = exports.hasEncoder = void 0; var hasEncoder = function (_a) { - var ffmpegPath = _a.ffmpegPath, encoder = _a.encoder, inputArgs = _a.inputArgs, filter = _a.filter; + var ffmpegPath = _a.ffmpegPath, encoder = _a.encoder, inputArgs = _a.inputArgs, filter = _a.filter, args = _a.args; return __awaiter(void 0, void 0, void 0, function () { var exec, isEnabled, err_1; return __generator(this, function (_b) { @@ -64,6 +64,8 @@ var hasEncoder = function (_a) { var command = "".concat(ffmpegPath, " ").concat(inputArgs.join(' ') || '', " -f lavfi -i color=c=black:s=256x256:d=1:r=30") + " ".concat(filter || '') + " -c:v ".concat(encoder, " -f null /dev/null"); + args.jobLog("Checking for encoder ".concat(encoder, " with command:")); + args.jobLog(command); exec(command, function ( // eslint-disable-next-line error) { @@ -76,6 +78,7 @@ var hasEncoder = function (_a) { })]; case 2: isEnabled = _b.sent(); + args.jobLog("Encoder ".concat(encoder, " is ").concat(isEnabled ? 'enabled' : 'disabled')); return [3 /*break*/, 4]; case 3: err_1 = _b.sent(); @@ -154,18 +157,21 @@ var encoderFilter = function (encoder, targetCodec) { if (targetCodec === 'h264' && encoder.includes('h264')) { return true; } + if (targetCodec === 'av1' && encoder.includes('av1')) { + return true; + } return false; }; var getEncoder = function (_a) { - var targetCodec = _a.targetCodec, hardwareEncoding = _a.hardwareEncoding, args = _a.args; + var targetCodec = _a.targetCodec, hardwareEncoding = _a.hardwareEncoding, hardwareType = _a.hardwareType, args = _a.args; return __awaiter(void 0, void 0, void 0, function () { - var gpuEncoders, filteredGpuEncoders, _i, filteredGpuEncoders_1, gpuEncoder, _b, enabledDevices, res; + var gpuEncoders, filteredGpuEncoders, idx, _i, filteredGpuEncoders_1, gpuEncoder, _b, enabledDevices, res; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!(args.workerType && args.workerType.includes('gpu') - && hardwareEncoding && (targetCodec === 'hevc' || targetCodec === 'h264'))) return [3 /*break*/, 5]; + && hardwareEncoding && (['hevc', 'h264', 'av1'].includes(targetCodec)))) return [3 /*break*/, 5]; gpuEncoders = [ { encoder: 'hevc_nvenc', @@ -184,6 +190,16 @@ var getEncoder = function (_a) { outputArgs: [], filter: '', }, + { + encoder: 'hevc_qsv', + enabled: false, + inputArgs: [ + '-hwaccel', + 'qsv', + ], + outputArgs: [], + filter: '', + }, { encoder: 'hevc_vaapi', inputArgs: [ @@ -198,16 +214,6 @@ var getEncoder = function (_a) { enabled: false, filter: '-vf format=nv12,hwupload', }, - { - encoder: 'hevc_qsv', - enabled: false, - inputArgs: [ - '-hwaccel', - 'qsv', - ], - outputArgs: [], - filter: '', - }, { encoder: 'hevc_videotoolbox', enabled: false, @@ -218,6 +224,7 @@ var getEncoder = function (_a) { outputArgs: [], filter: '', }, + // h264 { encoder: 'h264_nvenc', enabled: false, @@ -255,8 +262,45 @@ var getEncoder = function (_a) { outputArgs: [], filter: '', }, + // av1 + { + encoder: 'av1_nvenc', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'av1_amf', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'av1_qsv', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'av1_vaapi', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, ]; filteredGpuEncoders = gpuEncoders.filter(function (device) { return encoderFilter(device.encoder, targetCodec); }); + if (hardwareEncoding && hardwareType !== 'auto') { + idx = filteredGpuEncoders.findIndex(function (device) { return device.encoder.includes(hardwareType); }); + if (idx === -1) { + throw new Error("Could not find encoder ".concat(targetCodec, " for hardware ").concat(hardwareType)); + } + return [2 /*return*/, __assign(__assign({}, filteredGpuEncoders[idx]), { isGpu: true, enabledDevices: [] })]; + } + args.jobLog(JSON.stringify({ filteredGpuEncoders: filteredGpuEncoders })); _i = 0, filteredGpuEncoders_1 = filteredGpuEncoders; _c.label = 1; case 1: @@ -269,6 +313,7 @@ var getEncoder = function (_a) { encoder: gpuEncoder.encoder, inputArgs: gpuEncoder.inputArgs, filter: gpuEncoder.filter, + args: args, })]; case 2: // eslint-disable-next-line no-await-in-loop @@ -278,20 +323,22 @@ var getEncoder = function (_a) { _i++; return [3 /*break*/, 1]; case 4: - enabledDevices = gpuEncoders.filter(function (device) { return device.enabled === true; }); + enabledDevices = filteredGpuEncoders.filter(function (device) { return device.enabled === true; }); + args.jobLog(JSON.stringify({ enabledDevices: enabledDevices })); if (enabledDevices.length > 0) { if (enabledDevices[0].encoder.includes('nvenc')) { res = (0, exports.getBestNvencDevice)({ args: args, nvencDevice: enabledDevices[0], }); - return [2 /*return*/, __assign(__assign({}, res), { isGpu: true })]; + return [2 /*return*/, __assign(__assign({}, res), { isGpu: true, enabledDevices: enabledDevices })]; } return [2 /*return*/, { encoder: enabledDevices[0].encoder, inputArgs: enabledDevices[0].inputArgs, outputArgs: enabledDevices[0].outputArgs, isGpu: true, + enabledDevices: enabledDevices, }]; } _c.label = 5; @@ -302,6 +349,7 @@ var getEncoder = function (_a) { inputArgs: [], outputArgs: [], isGpu: false, + enabledDevices: [], }]; } if (targetCodec === 'h264') { @@ -310,6 +358,16 @@ var getEncoder = function (_a) { inputArgs: [], outputArgs: [], isGpu: false, + enabledDevices: [], + }]; + } + if (targetCodec === 'av1') { + return [2 /*return*/, { + encoder: 'libsvtav1', + inputArgs: [], + outputArgs: [], + isGpu: false, + enabledDevices: [], }]; } return [2 /*return*/, { @@ -317,6 +375,7 @@ var getEncoder = function (_a) { inputArgs: [], outputArgs: [], isGpu: false, + enabledDevices: [], }]; } }); diff --git a/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.test.js b/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.test.js index d3c0a491f..88def6d4d 100644 --- a/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.test.js +++ b/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.test.js @@ -44,6 +44,7 @@ var run = function () { return __awaiter(void 0, void 0, void 0, function () { case 0: return [4 /*yield*/, (0, hardwareUtils_1.getEncoder)({ targetCodec: 'h264', hardwareEncoding: true, + hardwareType: 'auto', // @ts-expect-error type args: { workerType: 'transcodegpu', diff --git a/FlowPluginsTs/CommunityFlowPlugins/audio/checkChannelCount/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/audio/checkChannelCount/1.0.0/index.ts new file mode 100644 index 000000000..4540cf131 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/audio/checkChannelCount/1.0.0/index.ts @@ -0,0 +1,87 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Check Channel Count', + description: 'Check streams for specified channel count', + style: { + borderColor: 'orange', + }, + tags: 'audio', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'channelCount', + type: 'number', + defaultValue: '2', + inputUI: { + type: 'dropdown', + options: [ + '1', + '2', + '6', + '8', + ], + }, + tooltip: 'Specify channel count to check for', + }, + + ], + outputs: [ + { + number: 1, + tooltip: 'File has stream with specified channel count', + }, + { + number: 2, + tooltip: 'File does not have stream with specified channel count', + }, + ], +}); + +// 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); + + const channelCount = Number(args.inputs.channelCount); + + let hasSpecifiedChannelCount = false; + + args.jobLog(`Checking for ${channelCount} channels`); + + if (Array.isArray(args?.inputFileObj?.ffProbeData?.streams)) { + for (let i = 0; i < args.inputFileObj.ffProbeData.streams.length; i += 1) { + const stream = args.inputFileObj.ffProbeData.streams[i]; + + args.jobLog(`Stream ${i} has ${stream.channels} channels`); + + if ( + stream.channels === channelCount + ) { + hasSpecifiedChannelCount = true; + } + } + } else { + throw new Error('File has no stream data'); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: hasSpecifiedChannelCount ? 1 : 2, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/audio/normalizeAudio/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/audio/normalizeAudio/1.0.0/index.ts index ab093491f..eabfbddd5 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/audio/normalizeAudio/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/audio/normalizeAudio/1.0.0/index.ts @@ -1,5 +1,5 @@ import { CLI } from '../../../../FlowHelpers/1.0.0/cliUtils'; -import { getContainer } from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { getContainer, getFileName, getPluginWorkDir } from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, IpluginInputArgs, @@ -15,6 +15,8 @@ const details = (): IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -69,7 +71,7 @@ const plugin = async (args: IpluginInputArgs): Promise => { const { tp } = args.inputs; const container = getContainer(args.inputFileObj._id); - const outputFilePath = `${args.workDir}/tempFile_${new Date().getTime()}.${container}`; + const outputFilePath = `${getPluginWorkDir(args)}/${getFileName(args.inputFileObj._id)}.${container}`; const normArgs1: string[] = [ '-i', diff --git a/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.ts index a8a773ee3..8eb33f8d1 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.ts @@ -1,9 +1,9 @@ -import { getContainer } from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, IpluginInputArgs, IpluginOutputArgs, } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; +import { runClassicPlugin } from '../../../../FlowHelpers/1.0.0/classicPlugins'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = (): IpluginDetails => ({ @@ -14,6 +14,8 @@ const details = (): IpluginDetails => ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ @@ -42,61 +44,12 @@ const details = (): IpluginDetails => ({ // eslint-disable-next-line @typescript-eslint/no-unused-vars const plugin = async (args: IpluginInputArgs): Promise => { - const path = require('path'); const lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); - const pluginSourceId = String(args.inputs.pluginSourceId); - const parts = pluginSourceId.split(':'); - const pluginSource = parts[0]; - const pluginId = parts[1]; - - const relativePluginPath = `../../../../../${pluginSource}/${pluginId}.js`; - const absolutePath = path.resolve(__dirname, relativePluginPath); - - let classicPlugin; - if (pluginSource === 'Community') { - classicPlugin = args.deps.importFresh(relativePluginPath); - } else { - // eslint-disable-next-line no-await-in-loop - const res = await args.deps.axiosMiddleware('api/v2/read-plugin', { - plugin: { - id: pluginId, - source: pluginSource, - }, - }); - - classicPlugin = args.deps.requireFromString(res.pluginRaw, absolutePath); - } - - if (classicPlugin.details().Operation !== 'Filter') { - throw new Error( - `${'This plugin is meant for classic plugins that have ' - + 'Operation: Filter. This classic plugin has Operation: '}${classicPlugin.details().Operation}` - + 'Please use the Run Classic Transcode Flow Plugin plugin instead.' - , - ); - } - - const container = getContainer(args.inputFileObj._id); - const cacheFilePath = `${args.workDir}/tempFile_${new Date().getTime()}.${container}`; - - const otherArguments = { - handbrakePath: args.handbrakePath, - ffmpegPath: args.ffmpegPath, - mkvpropeditPath: args.mkvpropeditPath, - originalLibraryFile: args.originalLibraryFile, - nodeHardwareType: args.nodeHardwareType, - pluginCycle: 0, - workerType: args.workerType, - version: args.config.version, - platform_arch_isdocker: args.platform_arch_isdocker, - cacheFilePath, - job: args.job, - }; - - const result = await classicPlugin.plugin(args.inputFileObj, args.librarySettings, args.inputs, otherArguments); + const outcome = await runClassicPlugin(args, 'filter'); + const { result } = outcome; args.jobLog(JSON.stringify(result, null, 2)); diff --git a/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.ts index 0fbd06a4e..654050581 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.ts @@ -1,10 +1,10 @@ import { CLI } from '../../../../FlowHelpers/1.0.0/cliUtils'; -import { getContainer } from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, IpluginInputArgs, IpluginOutputArgs, } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; +import { runClassicPlugin } from '../../../../FlowHelpers/1.0.0/classicPlugins'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = (): IpluginDetails => ({ @@ -15,6 +15,8 @@ const details = (): IpluginDetails => ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -45,61 +47,14 @@ const replaceContainer = (filePath:string, container:string): string => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const plugin = async (args: IpluginInputArgs): Promise => { - const path = require('path'); const lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); - const pluginSourceId = String(args.inputs.pluginSourceId); - const parts = pluginSourceId.split(':'); - const pluginSource = parts[0]; - const pluginId = parts[1]; + const outcome = await runClassicPlugin(args, 'transcode'); + const { result, absolutePath } = outcome; - const relativePluginPath = `../../../../../${pluginSource}/${pluginId}.js`; - const absolutePath = path.resolve(__dirname, relativePluginPath); - - let classicPlugin; - if (pluginSource === 'Community') { - classicPlugin = args.deps.importFresh(relativePluginPath); - } else { - // eslint-disable-next-line no-await-in-loop - const res = await args.deps.axiosMiddleware('api/v2/read-plugin', { - plugin: { - id: pluginId, - source: pluginSource, - }, - }); - - classicPlugin = args.deps.requireFromString(res.pluginRaw, absolutePath); - } - - if (classicPlugin.details().Operation === 'Filter') { - throw new Error( - `${'This plugin is meant for classic plugins that have ' - + 'Operation: Transcode. This classic plugin has Operation: '}${classicPlugin.details().Operation}` - + 'Please use the Run Classic Filter Flow Plugin plugin instead.' - , - ); - } - - const container = getContainer(args.inputFileObj._id); - let cacheFilePath = `${args.workDir}/tempFile_${new Date().getTime()}.${container}`; - - const otherArguments = { - handbrakePath: args.handbrakePath, - ffmpegPath: args.ffmpegPath, - mkvpropeditPath: args.mkvpropeditPath, - originalLibraryFile: args.originalLibraryFile, - nodeHardwareType: args.nodeHardwareType, - pluginCycle: 0, - workerType: args.workerType, - version: args.config.version, - platform_arch_isdocker: args.platform_arch_isdocker, - cacheFilePath, - job: args.job, - }; - - const result = await classicPlugin.plugin(args.inputFileObj, args.librarySettings, args.inputs, otherArguments); + let { cacheFilePath } = outcome; args.jobLog(JSON.stringify(result, null, 2)); @@ -154,11 +109,15 @@ const plugin = async (args: IpluginInputArgs): Promise => { const customArgs = result?.custom?.args; const isCustomConfig = (Array.isArray(customArgs) && customArgs.length > 0) - || (typeof customArgs === 'string' && customArgs.length > 0); + || (typeof customArgs === 'string' + // @ts-expect-error length + && customArgs.length + > 0); if (!isCustomConfig) { cacheFilePath = replaceContainer(cacheFilePath, result.container); } else { + // @ts-expect-error type cacheFilePath = result.custom.outputPath; } @@ -173,6 +132,7 @@ const plugin = async (args: IpluginInputArgs): Promise => { let cliPath = ''; if (isCustomConfig) { + // @ts-expect-error cliPath cliPath = result?.custom?.cliPath; if (Array.isArray(customArgs)) { diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.ts index ba54fd319..cc348a694 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.ts @@ -13,6 +13,8 @@ const details = () :IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.ts index c28801c67..9f05381fb 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = () :IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCustomArguments/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCustomArguments/1.0.0/index.ts index 52392a0f0..e5c8c998d 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCustomArguments/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCustomArguments/1.0.0/index.ts @@ -7,16 +7,37 @@ import { /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = () :IpluginDetails => ({ name: 'Custom Arguments', - description: 'Custom Arguments', + description: 'Set FFmpeg custome input and output arguments', style: { borderColor: '#6efefc', - opacity: 0.5, }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', - inputs: [], + inputs: [ + { + name: 'inputArguments', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Specify input arguments', + }, + + { + name: 'outputArguments', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Specify output arguments', + }, + ], outputs: [ { number: 1, @@ -31,6 +52,17 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); + const inputArguments = String(args.inputs.inputArguments); + const outputArguments = String(args.inputs.outputArguments); + + if (inputArguments) { + args.variables.ffmpegCommand.overallInputArguments.push(...inputArguments.split(' ')); + } + + if (outputArguments) { + args.variables.ffmpegCommand.overallOuputArguments.push(...outputArguments.split(' ')); + } + return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandEnsureAudioStream/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandEnsureAudioStream/1.0.0/index.ts index d7d9365c8..c937e0ab4 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandEnsureAudioStream/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandEnsureAudioStream/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = () :IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.ts index b524c164d..4ec2b6cf7 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.ts @@ -5,6 +5,7 @@ import { IpluginOutputArgs, } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; import { CLI } from '../../../../FlowHelpers/1.0.0/cliUtils'; +import { getFileName, getPluginWorkDir } from '../../../../FlowHelpers/1.0.0/fileUtils'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = (): IpluginDetails => ({ @@ -16,6 +17,8 @@ const details = (): IpluginDetails => ({ tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: 2, icon: 'faPlay', inputs: [], @@ -71,7 +74,9 @@ const plugin = async (args: IpluginInputArgs): Promise => { cliArgs.push('-i'); cliArgs.push(args.inputFileObj._id); - const inputArgs: string[] = []; + const inputArgs: string[] = [ + ...args.variables.ffmpegCommand.overallInputArguments, + ]; let { shouldProcess, streams } = args.variables.ffmpegCommand; streams = streams.filter((stream) => { @@ -120,8 +125,11 @@ const plugin = async (args: IpluginInputArgs): Promise => { const idx = cliArgs.indexOf('-i'); cliArgs.splice(idx, 0, ...inputArgs); + cliArgs.push(...args.variables.ffmpegCommand.overallOuputArguments); + + const outputFilePath = `${getPluginWorkDir(args)}/${getFileName(args.inputFileObj._id)}` + + `.${args.variables.ffmpegCommand.container}`; - const outputFilePath = `${args.workDir}/tempFile_${new Date().getTime()}.${args.variables.ffmpegCommand.container}`; cliArgs.push(outputFilePath); args.jobLog('Processing file'); diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.ts index 571671e6c..16022748d 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.ts @@ -13,6 +13,8 @@ const details = () :IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.ts index 2b79edea1..fd925f3d3 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = () :IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.ts index dbf9aa9db..041da601d 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.ts @@ -16,6 +16,8 @@ const details = ():IpluginDetails => ({ tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.ts index 60ac7a8e7..f25661563 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.ts @@ -15,6 +15,8 @@ const details = ():IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.ts index 153ae1489..02e0283bc 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.ts @@ -15,6 +15,8 @@ const details = (): IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.ts index 15e83abd6..e5beb15c2 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.ts @@ -8,7 +8,7 @@ import { } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; /* eslint-disable no-param-reassign */ -const details = ():IpluginDetails => ({ +const details = (): IpluginDetails => ({ name: 'Set Container', description: 'Set the container of the output file', style: { @@ -16,6 +16,8 @@ const details = ():IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -32,6 +34,23 @@ const details = ():IpluginDetails => ({ }, tooltip: 'Specify the container to use', }, + { + name: 'forceConform', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'dropdown', + options: [ + 'false', + 'true', + ], + }, + tooltip: ` +Specify if you want to force conform the file to the new container, +This is useful if not all streams are supported by the new container. +For example mkv does not support data streams. + `, + }, ], outputs: [ { @@ -42,16 +61,55 @@ 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); const newContainer = String(args.inputs.container); + const { forceConform } = args.inputs; - if (getContainer(args.inputFileObj._id) !== args.inputs.container) { + if (getContainer(args.inputFileObj._id) !== newContainer) { args.variables.ffmpegCommand.container = newContainer; args.variables.ffmpegCommand.shouldProcess = true; + + if (forceConform === true) { + for (let i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { + const stream = args.variables.ffmpegCommand.streams[i]; + + try { + const codecType = stream.codec_type.toLowerCase(); + const codecName = stream.codec_name.toLowerCase(); + if (newContainer === 'mkv') { + if ( + codecType === 'data' + || [ + 'mov_text', + 'eia_608', + 'timed_id3', + ].includes(codecName) + ) { + stream.removed = true; + } + } + + if (newContainer === 'mp4') { + if ( + [ + 'hdmv_pgs_subtitle', + 'eia_608', + 'timed_id3', + 'subrip', + ].includes(codecName) + ) { + stream.removed = true; + } + } + } catch (err) { + // Error + } + } + } } return { diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoFramerate/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoFramerate/1.0.0/index.ts new file mode 100644 index 000000000..fdd8be4e0 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoFramerate/1.0.0/index.ts @@ -0,0 +1,93 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Set Video Framerate', + description: 'Set Video Framerate. If the original framerate is lower than the' + + ' specified framerate, the original framerate will be used.', + style: { + borderColor: '#6efefc', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'framerate', + type: 'number', + defaultValue: '30', + inputUI: { + type: 'text', + }, + tooltip: 'Specify framerate value', + }, + ], + 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); + + const desiredFrameRate = Number(args.inputs.framerate); + + args.jobLog(`Desired framerate: ${desiredFrameRate}`); + + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type === 'video') { + let fileFramerateUsed = false; + + if (stream.avg_frame_rate) { + const parts = stream.avg_frame_rate.split('/'); + + if (parts.length === 2) { + const numerator = parseInt(parts[0], 10); + const denominator = parseInt(parts[1], 10); + + if (numerator > 0 && denominator > 0) { + const fileFramerate = numerator / denominator; + + args.jobLog(`File framerate: ${fileFramerate}`); + + if (fileFramerate < desiredFrameRate) { + args.jobLog('File framerate is lower than desired framerate. Using file framerate.'); + stream.outputArgs.push('-r', `${String(fileFramerate)}`); + fileFramerateUsed = true; + } else { + args.jobLog('File framerate is greater than desired framerate. Using desired framerate.'); + } + } + } + } + + if (!fileFramerateUsed) { + args.jobLog('Using desired framerate.'); + stream.outputArgs.push('-r', `${String(desiredFrameRate)}`); + } + } + }); + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.ts index 3190f7249..a73302849 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.ts @@ -13,6 +13,8 @@ const details = () :IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.ts index a7c548143..4e21bedec 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = () :IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.ts index 6f312863e..094f233e1 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.ts @@ -16,6 +16,8 @@ const details = (): IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -30,6 +32,7 @@ const details = (): IpluginDetails => ({ // 'vp9', 'h264', // 'vp8', + 'av1', ], }, tooltip: 'Specify codec of the output file', @@ -76,6 +79,22 @@ const details = (): IpluginDetails => ({ }, tooltip: 'Specify whether to use hardware encoding if available', }, + { + name: 'hardwareType', + type: 'string', + defaultValue: 'auto', + inputUI: { + type: 'dropdown', + options: [ + 'auto', + 'nvenc', + 'qsv', + 'vaapi', + 'videotoolbox', + ], + }, + tooltip: 'Specify codec of the output file', + }, { name: 'hardwareDecoding', type: 'boolean', @@ -92,7 +111,7 @@ const details = (): IpluginDetails => ({ { name: 'forceEncoding', type: 'boolean', - defaultValue: 'false', + defaultValue: 'true', inputUI: { type: 'dropdown', options: [ @@ -118,6 +137,7 @@ const plugin = async (args: IpluginInputArgs): Promise => { args.inputs = lib.loadDefaultValues(args.inputs, details); const hardwareDecoding = args.inputs.hardwareDecoding === true; + const hardwareType = String(args.inputs.hardwareType); args.variables.ffmpegCommand.hardwareDecoding = hardwareDecoding; for (let i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { @@ -140,6 +160,7 @@ const plugin = async (args: IpluginInputArgs): Promise => { const encoderProperties = await getEncoder({ targetCodec, hardwareEncoding: hardwarEncoding, + hardwareType, args, }); @@ -151,7 +172,7 @@ const plugin = async (args: IpluginInputArgs): Promise => { stream.outputArgs.push('-crf', ffmpegQuality); } - if (ffmpegPreset) { + if (targetCodec !== 'av1' && ffmpegPreset) { stream.outputArgs.push('-preset', ffmpegPreset); } diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.ts index d4d1c8874..a2824072a 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.ts @@ -18,6 +18,8 @@ const details = () :IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: 1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExists/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExists/1.0.0/index.ts new file mode 100644 index 000000000..2b5558155 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExists/1.0.0/index.ts @@ -0,0 +1,89 @@ +import fs from 'fs'; +import { getContainer, getFileAbosluteDir, getFileName } from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Check File Exists', + description: 'Check file Exists', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'fileToCheck', + type: 'string', + // eslint-disable-next-line no-template-curly-in-string + defaultValue: '${fileName}_720p.${container}', + inputUI: { + type: 'text', + }, + // eslint-disable-next-line no-template-curly-in-string + tooltip: 'Specify file to check using templating e.g. ${fileName}_720p.${container}', + }, + { + name: 'directory', + type: 'string', + defaultValue: '', + inputUI: { + type: 'directory', + }, + tooltip: 'Specify directory to check. Leave blank to use working directory.' + + ' Put below Input File plugin to check original file directory.', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File exists', + }, + { + number: 2, + tooltip: 'File does not exist', + }, + ], +}); + +// 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); + + const directory = String(args.inputs.directory).trim() || getFileAbosluteDir(args.inputFileObj._id); + + const fileName = getFileName(args.inputFileObj._id); + + let fileToCheck = String(args.inputs.fileToCheck).trim(); + fileToCheck = fileToCheck.replace(/\${fileName}/g, fileName); + fileToCheck = fileToCheck.replace(/\${container}/g, getContainer(args.inputFileObj._id)); + fileToCheck = `${directory}/${fileToCheck}`; + + let fileExists = false; + if (fs.existsSync(fileToCheck)) { + fileExists = true; + args.jobLog(`File exists: ${fileToCheck}`); + } else { + args.jobLog(`File does not exist: ${fileToCheck}`); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: fileExists ? 1 : 2, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.ts index 4cec839a8..1149b1cf1 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = (): IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.ts index c3ac47eb5..3fc1ebf62 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = (): IpluginDetails => ({ tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/checkFileNameIncludes/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileNameIncludes/1.0.0/index.ts new file mode 100644 index 000000000..21680e8c8 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileNameIncludes/1.0.0/index.ts @@ -0,0 +1,72 @@ +import { getContainer, getFileName } from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Check File Name Includes', + description: 'Check if a file name includes specific terms. Only needs to match one term', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'terms', + type: 'string', + // eslint-disable-next-line no-template-curly-in-string + defaultValue: '_720p,_1080p', + inputUI: { + type: 'text', + }, + // eslint-disable-next-line no-template-curly-in-string + tooltip: 'Specify terms to check for in file name using comma seperated list e.g. _720p,_1080p', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File name contains terms', + }, + { + number: 2, + tooltip: 'File name does not contains terms', + }, + ], +}); + +// 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); + + const fileName = `${getFileName(args.inputFileObj._id)}.${getContainer(args.inputFileObj._id)}`; + const terms = String(args.inputs.terms).trim().split(','); + let containsTerms = false; + + for (let i = 0; i < terms.length; i++) { + if (fileName.includes(terms[i])) { + containsTerms = true; + break; + } + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: containsTerms ? 1 : 2, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.ts index 04043c512..b43505362 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.ts @@ -13,6 +13,8 @@ const details = (): IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.ts index b9ab91bea..c1a041e14 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = (): IpluginDetails => ({ tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSizeRatio/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSizeRatio/1.0.0/index.ts new file mode 100644 index 000000000..dfcd7148f --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSizeRatio/1.0.0/index.ts @@ -0,0 +1,96 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Compare File Size Ratio', + description: 'Compare file size ratio of working file compared to original file using percentage.', + style: { + borderColor: 'orange', + }, + tags: '', + + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'greaterThan', + type: 'number', + defaultValue: '40', + inputUI: { + type: 'text', + }, + tooltip: 'Specify lower bound. ' + + 'Default value is 40% so new file size must be at least 40% of original file size.', + }, + { + name: 'lessThan', + type: 'number', + defaultValue: '110', + inputUI: { + type: 'text', + }, + tooltip: 'Specify upper bound.' + + ' Default value is 110% so new file size must be at most 110% of original file size.', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Working file size % is within range', + }, + { + number: 2, + tooltip: 'Working file size % is not within range', + }, + ], +}); + +// 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); + + let isWithinRange = false; + const newFileSizeBytes = args.inputFileObj.file_size; + const origFileSizeBytes = args.originalLibraryFile.file_size; + + const greaterThanPerc = Number(args.inputs.greaterThan); + const lessThanPerc = Number(args.inputs.lessThan); + + const ratio = (newFileSizeBytes / origFileSizeBytes) * 100; + + const sizeText = `New file has size ${newFileSizeBytes.toFixed(3)} MB which is ${ratio}% ` + + `of original file size: ${origFileSizeBytes.toFixed(3)} MB`; + + const getBound = (bound:number) => (bound / 100) * origFileSizeBytes; + + const errText = 'New file size not within limits.'; + if (newFileSizeBytes > getBound(lessThanPerc)) { + // Item will be errored in UI + args.jobLog(`${errText} ${sizeText}. upperBound is ${lessThanPerc}%`); + } else if (newFileSizeBytes < getBound(greaterThanPerc)) { + // // Item will be errored in UI + args.jobLog(`${errText} ${sizeText}. lowerBound is ${greaterThanPerc}%`); + } else { + args.jobLog(sizeText); + isWithinRange = true; + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSizeRatio/2.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSizeRatio/2.0.0/index.ts new file mode 100644 index 000000000..c3dd7f057 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSizeRatio/2.0.0/index.ts @@ -0,0 +1,102 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Compare File Size Ratio', + description: 'Compare file size ratio of working file compared to original file using percentage.', + style: { + borderColor: 'orange', + }, + tags: '', + + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'greaterThan', + type: 'number', + defaultValue: '40', + inputUI: { + type: 'text', + }, + tooltip: 'Specify lower bound.' + + 'Default value is 40% so new file size must be at least 40% of original file size.', + }, + { + name: 'lessThan', + type: 'number', + defaultValue: '110', + inputUI: { + type: 'text', + }, + tooltip: 'Specify upper bound.' + + ' Default value is 110% so new file size must be at most 110% of original file size.', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Working file size % is within range', + }, + { + number: 2, + tooltip: 'Working file size % is smaller than lower bound', + }, + { + number: 3, + tooltip: 'Working file size % is larger than upper bound', + }, + ], +}); + +// 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); + + const newFileSizeBytes = args.inputFileObj.file_size; + const origFileSizeBytes = args.originalLibraryFile.file_size; + + const greaterThanPerc = Number(args.inputs.greaterThan); + const lessThanPerc = Number(args.inputs.lessThan); + + const ratio = (newFileSizeBytes / origFileSizeBytes) * 100; + + const sizeText = `New file has size ${newFileSizeBytes.toFixed(3)} MB which is ${ratio}% ` + + `of original file size: ${origFileSizeBytes.toFixed(3)} MB`; + + const getBound = (bound:number) => (bound / 100) * origFileSizeBytes; + + let outputNumber = 1; + + const errText = 'New file size not within limits.'; + if (newFileSizeBytes > getBound(lessThanPerc)) { + // Item will be errored in UI + args.jobLog(`${errText} ${sizeText}. upperBound is ${lessThanPerc}%`); + outputNumber = 3; + } else if (newFileSizeBytes < getBound(greaterThanPerc)) { + // // Item will be errored in UI + args.jobLog(`${errText} ${sizeText}. lowerBound is ${greaterThanPerc}%`); + outputNumber = 2; + } else { + args.jobLog(sizeText); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.ts index eaa0ef709..92aec6551 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.ts @@ -17,6 +17,8 @@ const details = (): IpluginDetails => ({ tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [ @@ -34,7 +36,7 @@ const details = (): IpluginDetails => ({ type: 'boolean', defaultValue: 'false', inputUI: { - type: 'text', + type: 'dropdown', options: [ 'false', 'true', @@ -115,6 +117,18 @@ const plugin = async (args: IpluginInputArgs): Promise => { args.jobLog(`Input path: ${args.inputFileObj._id}`); args.jobLog(`Output path: ${outputPath}`); + if (args.inputFileObj._id === ouputFilePath) { + args.jobLog('Input and output path are the same, skipping copy.'); + + return { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }; + } + args.deps.fsextra.ensureDirSync(outputPath); await fs.copyFile(args.inputFileObj._id, ouputFilePath); diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/copyToWorkDirectory/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/copyToWorkDirectory/1.0.0/index.ts index c39cf4d36..04458d1f8 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/copyToWorkDirectory/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/copyToWorkDirectory/1.0.0/index.ts @@ -18,6 +18,8 @@ const details = (): IpluginDetails => ({ tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [], @@ -51,6 +53,18 @@ const plugin = async (args: IpluginInputArgs): Promise => { args.jobLog(`Input path: ${args.inputFileObj._id}`); args.jobLog(`Output path: ${outputPath}`); + if (args.inputFileObj._id === ouputFilePath) { + args.jobLog('Input and output path are the same, skipping copy.'); + + return { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }; + } + args.deps.fsextra.ensureDirSync(outputPath); await fs.copyFile(args.inputFileObj._id, ouputFilePath); diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.ts index c3348cd1a..1184e920c 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = ():IpluginDetails => ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts index cd276c619..cc3465888 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts @@ -18,6 +18,8 @@ const details = ():IpluginDetails => ({ tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [ @@ -35,7 +37,7 @@ const details = ():IpluginDetails => ({ type: 'boolean', defaultValue: 'false', inputUI: { - type: 'text', + type: 'dropdown', options: [ 'false', 'true', @@ -97,6 +99,18 @@ const plugin = async (args:IpluginInputArgs):Promise => { args.jobLog(`Input path: ${args.inputFileObj._id}`); args.jobLog(`Output path: ${ouputFilePath}`); + if (args.inputFileObj._id === ouputFilePath) { + args.jobLog('Input and output path are the same, skipping move.'); + + return { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }; + } + args.deps.fsextra.ensureDirSync(outputPath); await moveFileAndValidate({ diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.ts new file mode 100644 index 000000000..693027a9a --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.ts @@ -0,0 +1,74 @@ +import { + getContainer, getFileAbosluteDir, getFileName, moveFileAndValidate, +} from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = ():IpluginDetails => ({ + name: 'Move To Original Directory', + description: 'Move working file original directory.', + style: { + borderColor: 'green', + }, + tags: '', + + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faArrowRight', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = async (args:IpluginInputArgs):Promise => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + const fileName = getFileName(args.inputFileObj._id); + const container = getContainer(args.inputFileObj._id); + const outputDir = getFileAbosluteDir(args.originalLibraryFile._id); + + const ouputFilePath = `${outputDir}/${fileName}.${container}`; + + if (args.inputFileObj._id === ouputFilePath) { + args.jobLog('Input and output path are the same, skipping move.'); + + return { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }; + } + + await moveFileAndValidate({ + inputPath: args.inputFileObj._id, + outputPath: ouputFilePath, + args, + }); + + return { + outputFileObj: { + _id: ouputFilePath, + }, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/renameFile/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/renameFile/1.0.0/index.ts new file mode 100644 index 000000000..81b21a3b7 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/renameFile/1.0.0/index.ts @@ -0,0 +1,88 @@ +import { + getContainer, getFileAbosluteDir, getFileName, moveFileAndValidate, +} from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Rename File', + description: 'Rename a file', + style: { + borderColor: 'green', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'fileRename', + type: 'string', + // eslint-disable-next-line no-template-curly-in-string + defaultValue: '${fileName}_720p.${container}', + inputUI: { + type: 'text', + }, + // eslint-disable-next-line no-template-curly-in-string + tooltip: 'Specify file to check using templating e.g. ${fileName}_720p.${container}', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = async (args: IpluginInputArgs): Promise => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + const fileName = getFileName(args.inputFileObj._id); + + let newName = String(args.inputs.fileRename).trim(); + newName = newName.replace(/\${fileName}/g, fileName); + newName = newName.replace(/\${container}/g, getContainer(args.inputFileObj._id)); + + const fileDir = getFileAbosluteDir(args.inputFileObj._id); + const newPath = `${fileDir}/${newName}`; + + if (args.inputFileObj._id === newPath) { + args.jobLog('Input and output path are the same, skipping rename.'); + + return { + outputFileObj: { + _id: args.inputFileObj._id, + }, + outputNumber: 1, + variables: args.variables, + }; + } + + await moveFileAndValidate({ + inputPath: args.inputFileObj._id, + outputPath: newPath, + args, + }); + + return { + outputFileObj: { + _id: newPath, + }, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts index f38e7f6b8..8f4f68c2e 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts @@ -1,4 +1,6 @@ -import { moveFileAndValidate } from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { + getContainer, getFileAbosluteDir, getFileName, moveFileAndValidate, +} from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, IpluginInputArgs, @@ -8,12 +10,14 @@ import { /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = (): IpluginDetails => ({ name: 'Replace Original File', - description: 'Replace the original file', + description: 'Replace the original file. If the file hasn\'t changed then no action is taken.', style: { borderColor: 'green', }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [], @@ -25,16 +29,6 @@ const details = (): IpluginDetails => ({ ], }); -const getNewPath = (originalPath: string, tempPath: string) => { - const tempPathParts = tempPath.split('.'); - const container = tempPathParts[tempPathParts.length - 1]; - - const originalPathParts = originalPath.split('.'); - - originalPathParts[originalPathParts.length - 1] = container; - return originalPathParts.join('.'); -}; - // eslint-disable-next-line @typescript-eslint/no-unused-vars const plugin = async (args: IpluginInputArgs): Promise => { const fs = require('fs'); @@ -57,7 +51,11 @@ const plugin = async (args: IpluginInputArgs): Promise => { args.jobLog('File has changed, replacing original file'); const currentPath = args.inputFileObj._id; - const newPath = getNewPath(args.originalLibraryFile._id, currentPath); + const orignalFolder = getFileAbosluteDir(args.originalLibraryFile._id); + const fileName = getFileName(args.inputFileObj._id); + const container = getContainer(args.inputFileObj._id); + + const newPath = `${orignalFolder}/${fileName}.${container}`; const newPathTmp = `${newPath}.tmp`; args.jobLog(JSON.stringify({ @@ -68,11 +66,6 @@ const plugin = async (args: IpluginInputArgs): Promise => { await new Promise((resolve) => setTimeout(resolve, 2000)); - // delete temp file - if (fs.existsSync(newPath)) { - fs.unlinkSync(newPath); - } - await moveFileAndValidate({ inputPath: currentPath, outputPath: newPathTmp, @@ -80,7 +73,11 @@ const plugin = async (args: IpluginInputArgs): Promise => { }); // delete original file - if (fs.existsSync(args.originalLibraryFile._id)) { + if ( + fs.existsSync(args.originalLibraryFile._id) + && args.originalLibraryFile._id !== currentPath + ) { + args.jobLog(`Deleting original file:${args.originalLibraryFile._id}`); fs.unlinkSync(args.originalLibraryFile._id); } diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.ts index 4ae3b77eb..3c0d4ff2b 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.ts @@ -13,6 +13,8 @@ const details = ():IpluginDetails => ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/unpack/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/unpack/1.0.0/index.ts index fbff15535..bac4c3881 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/unpack/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/unpack/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = ():IpluginDetails => ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.ts index b5485dc98..7ed495b5f 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.ts @@ -5,7 +5,7 @@ import { IpluginInputArgs, IpluginOutputArgs, } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; -import { getContainer } from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { getContainer, getFileName, getPluginWorkDir } from '../../../../FlowHelpers/1.0.0/fileUtils'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = ():IpluginDetails => ({ @@ -16,6 +16,8 @@ const details = ():IpluginDetails => ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ @@ -79,7 +81,7 @@ const plugin = async (args:IpluginInputArgs):Promise => { container = getContainer(args.inputFileObj._id); } - const outputFilePath = `${args.workDir}/tempFile_${new Date().getTime()}.${container}`; + const outputFilePath = `${getPluginWorkDir(args)}/${getFileName(args.inputFileObj._id)}.${container}`; const presetString = String(args.inputs.jsonPreset); diff --git a/FlowPluginsTs/CommunityFlowPlugins/input/inputFile/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/input/inputFile/1.0.0/index.ts index 299cab941..e638af895 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/input/inputFile/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/input/inputFile/1.0.0/index.ts @@ -12,8 +12,9 @@ const details = ():IpluginDetails => ({ borderColor: 'pink', }, tags: '', - isStartPlugin: true, + pType: 'start', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/checkFlowVariable/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/checkFlowVariable/1.0.0/index.ts new file mode 100644 index 000000000..11a8b7593 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/checkFlowVariable/1.0.0/index.ts @@ -0,0 +1,136 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Check Flow Variable', + description: 'Check Flow Variable', + style: { + borderColor: 'orange', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'variable', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Variable to check. For example args.librarySettings._id', + }, + { + name: 'condition', + type: 'string', + defaultValue: '', + inputUI: { + type: 'dropdown', + options: [ + '==', + '!=', + ], + }, + tooltip: 'Check condition', + }, + + { + name: 'value', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Value of variable to check', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'The variable matches the condition', + }, + { + number: 2, + tooltip: 'The variable does not match the condition', + }, + ], +}); + +// 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); + + const variable = String(args.inputs.variable); + const condition = String(args.inputs.condition); + const value = String(args.inputs.value); + + // variable could be e.g. args.librarySettings._id or args.inputFileObj._id + // condition could be e.g. '==' or '!=' + + const variableParts = variable.split('.'); + + let targetValue; + switch (variableParts.length) { + case 1: + targetValue = args; + break; + case 2: + // @ts-expect-error index + targetValue = args[variableParts[1]]; + break; + case 3: + // @ts-expect-error index + targetValue = args[variableParts[1]][variableParts[2]]; + break; + case 4: + // @ts-expect-error index + targetValue = args[variableParts[1]][variableParts[2]][variableParts[3]]; + break; + case 5: + // @ts-expect-error index + targetValue = args[variableParts[1]][variableParts[2]][variableParts[3]][variableParts[4]]; + break; + default: + throw new Error(`Invalid variable: ${variable}`); + } + + targetValue = String(targetValue); + let outputNumber = 1; + + if (condition === '==') { + if (targetValue === value) { + args.jobLog(`Variable ${variable} of value ${targetValue} matches condition ${condition} ${value}`); + outputNumber = 1; + } else { + args.jobLog(`Variable ${variable} of value ${targetValue} does not match condition ${condition} ${value}`); + outputNumber = 2; + } + } else if (condition === '!=') { + if (targetValue !== value) { + args.jobLog(`Variable ${variable} of value ${targetValue} matches condition ${condition} ${value}`); + outputNumber = 1; + } else { + args.jobLog(`Variable ${variable} of value ${targetValue} does not match condition ${condition} ${value}`); + outputNumber = 2; + } + } + + return { + outputFileObj: args.inputFileObj, + outputNumber, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/checkNodeHardwareEncoder/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/checkNodeHardwareEncoder/1.0.0/index.ts new file mode 100644 index 000000000..e56206498 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/checkNodeHardwareEncoder/1.0.0/index.ts @@ -0,0 +1,89 @@ +import { getEncoder } from '../../../../FlowHelpers/1.0.0/hardwareUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Check Node Hardware Encoder', + description: ` + Check if node hardware encoder is available. Can also be used to check for specific hardware. + For example: + + hevc_nvenc = Nvidia + hevc_amf = AMD + hevc_vaapi = Intel + hevc_qsv = Intel + hevc_videotoolbox = Apple + `, + style: { + borderColor: 'orange', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'hardwareEncoder', + type: 'string', + defaultValue: 'hevc_nvenc', + inputUI: { + type: 'dropdown', + options: [ + 'hevc_nvenc', + 'hevc_amf', + 'hevc_vaapi', + 'hevc_qsv', + 'hevc_videotoolbox', + ], + }, + tooltip: 'Specify hardware (based on encoder) to check for', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Node has hardware', + }, + { + number: 2, + tooltip: 'Node does not have hardware', + }, + ], +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = async (args: IpluginInputArgs): Promise => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + const { hardwareEncoder } = args.inputs; + + // eslint-disable-next-line no-await-in-loop + const encoderProperties = await getEncoder({ + targetCodec: 'hevc', + hardwareEncoding: true, + hardwareType: 'auto', + args, + }); + + const nodeHasHardware = encoderProperties.enabledDevices.some((row) => row.encoder === hardwareEncoder); + + args.jobLog(`Node has hardwareEncoder ${hardwareEncoder}: ${nodeHasHardware}`); + + return { + outputFileObj: args.inputFileObj, + outputNumber: nodeHasHardware ? 1 : 2, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/comment/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/comment/1.0.0/index.ts new file mode 100644 index 000000000..60d851276 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/comment/1.0.0/index.ts @@ -0,0 +1,53 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Comment', + nameUI: { + type: 'textarea', + style: { + height: '250px', + }, + }, + description: `Add a comment to your flow. Can place anywhere and link together. + Any file input into the comment will be passed straight through.`, + style: { + borderColor: 'white', + borderRadius: '10px', + backgroundColor: '#043775', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faComment', + 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, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/failFlow/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/failFlow/1.0.0/index.ts index 7cccee81e..a39cb0ccf 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/tools/failFlow/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/failFlow/1.0.0/index.ts @@ -13,6 +13,8 @@ const details = ():IpluginDetails => ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faExclamationTriangle', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.ts index 235ab7552..51eb7f6f7 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.ts @@ -9,14 +9,26 @@ const details = ():IpluginDetails => ({ name: 'Go To Flow', description: 'Go to a different flow', style: { - borderColor: 'red', - opacity: 0.5, + borderColor: 'green', }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', - inputs: [], + inputs: [ + { + name: 'flowId', + type: 'string', + defaultValue: '', + inputUI: { + type: 'dropdown', + options: [], + }, + tooltip: 'Specify flow ID to go to', + }, + ], outputs: [], }); diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/onFlowError/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/onFlowError/1.0.0/index.ts new file mode 100644 index 000000000..1546bc189 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/onFlowError/1.0.0/index.ts @@ -0,0 +1,45 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = ():IpluginDetails => ({ + name: 'On Flow Error', + description: `Runs if an error occurs in this specific flow. + Won't run if error occurs in after going to a different flow (unless that flow comes back to this one).`, + style: { + borderColor: 'red', + }, + tags: '', + isStartPlugin: false, + pType: 'onFlowError', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faArrowRight', + 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, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/requireReview/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/requireReview/1.0.0/index.ts new file mode 100644 index 000000000..053e0d15b --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/requireReview/1.0.0/index.ts @@ -0,0 +1,46 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Require Review', + description: `Makes the flow pause. + The file will stay in the staging section on the Tdarr tab until the user clicks the "Reviewed" button. + `, + style: { + borderColor: 'yellow', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faHand', + 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, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/resetFlowError/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/resetFlowError/1.0.0/index.ts new file mode 100644 index 000000000..a37f4223f --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/resetFlowError/1.0.0/index.ts @@ -0,0 +1,48 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = ():IpluginDetails => ({ + name: 'Reset Flow Error', + description: `After a flow error occurs, this plugin will reset the flow +error so that the flow will not go to error status at the end of the flow.`, + style: { + borderColor: 'red', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faUndo', + 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); + + // eslint-disable-next-line no-param-reassign + args.variables.flowFailed = false; + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.ts index 1b3928341..34167d972 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.ts @@ -15,6 +15,8 @@ const details = ():IpluginDetails => ({ }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [], diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/waitTimeout/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/waitTimeout/1.0.0/index.ts new file mode 100644 index 000000000..f61d15fd4 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/waitTimeout/1.0.0/index.ts @@ -0,0 +1,106 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Wait', + description: 'Wait for a specified amount of time before continuing to the next plugin', + style: { + borderColor: 'yellow', + }, + tags: '', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faClock', + inputs: [ + { + name: 'amount', + type: 'string', + defaultValue: '1', + inputUI: { + type: 'text', + }, + tooltip: 'Specify the amount of time to wait', + }, + { + name: 'unit', + type: 'string', + defaultValue: 'seconds', + inputUI: { + type: 'dropdown', + options: [ + 'seconds', + 'minutes', + 'hours', + ], + }, + tooltip: 'Specify the unit of time to wait', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = async (args: IpluginInputArgs): Promise => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + const { + amount, + unit, + } = args.inputs; + + const amountNum = Number(amount); + if (Number.isNaN(amountNum)) { + throw new Error('Amount must be a number'); + } + let multiplier = 1; + if (unit === 'seconds') { + multiplier = 1000; + } else if (unit === 'minutes') { + multiplier = 60000; + } else if (unit === 'hours') { + multiplier = 3600000; + } + + const waitTime = amountNum * multiplier; + + args.jobLog(`Waiting for ${amount} ${unit}`); + args.jobLog(`Waiting for ${waitTime} milliseconds`); + + let finished = false; + + const logWait = () => { + if (!finished) { + args.jobLog('Waiting...'); + setTimeout(logWait, 5000); + } + }; + + logWait(); + + await new Promise((resolve) => setTimeout(resolve, waitTime)); + + finished = true; + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/webRequest/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/webRequest/1.0.0/index.ts index 4026f1254..76c19a26c 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/tools/webRequest/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/webRequest/1.0.0/index.ts @@ -5,18 +5,72 @@ import { } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ -const details = ():IpluginDetails => ({ +const details = (): IpluginDetails => ({ name: 'Send Web Request', description: 'Send Web Request', style: { borderColor: 'green', - opacity: 0.5, }, tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faArrowRight', - inputs: [], + inputs: [ + { + name: 'method', + type: 'string', + defaultValue: 'post', + inputUI: { + type: 'dropdown', + options: [ + 'get', + 'post', + 'put', + 'delete', + ], + }, + tooltip: 'Specify request method', + }, + { + name: 'requestUrl', + type: 'string', + defaultValue: 'http://example.com', + inputUI: { + type: 'text', + }, + tooltip: 'Specify request URL', + }, + { + name: 'requestHeaders', + type: 'string', + defaultValue: `{ + "Content-Type": "application/json" +}`, + inputUI: { + type: 'textarea', + style: { + height: '100px', + }, + }, + tooltip: 'Specify request URL', + }, + { + name: 'requestBody', + type: 'string', + defaultValue: `{ + "test": "test" +}`, + inputUI: { + type: 'textarea', + style: { + height: '100px', + }, + }, + tooltip: 'Specify request body', + }, + ], outputs: [ { number: 1, @@ -26,11 +80,32 @@ const details = ():IpluginDetails => ({ }); // eslint-disable-next-line @typescript-eslint/no-unused-vars -const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { +const plugin = async (args: IpluginInputArgs): Promise => { const lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); + const method = String(args.inputs.method); + const requestUrl = String(args.inputs.requestUrl); + const requestHeaders = JSON.parse(String(args.inputs.requestHeaders)); + const requestBody = JSON.parse(String(args.inputs.requestBody)); + + const requestConfig = { + method, + url: requestUrl, + headers: requestHeaders, + data: requestBody, + }; + + try { + const res = await args.deps.axios(requestConfig); + args.jobLog(`Web request succeeded: Status Code: ${res.status}`); + } catch (err) { + args.jobLog('Web Request Failed'); + args.jobLog(JSON.stringify(err)); + throw new Error('Web Request Failed'); + } + return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/CheckVideoFramerate/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/CheckVideoFramerate/1.0.0/index.ts new file mode 100644 index 000000000..98a280f58 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/video/CheckVideoFramerate/1.0.0/index.ts @@ -0,0 +1,88 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Check Video Framerate', + description: 'Check if video framerate is within a specific range', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'greaterThan', + type: 'number', + defaultValue: '0', + inputUI: { + type: 'text', + }, + tooltip: 'Specify lower bound of fps', + }, + { + name: 'lessThan', + type: 'number', + defaultValue: '60', + inputUI: { + type: 'text', + }, + tooltip: 'Specify upper bound fps', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File within range', + }, + { + number: 2, + tooltip: 'File not within range', + }, + ], +}); + +// 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); + + let isWithinRange = false; + + const greaterThanFps = Number(args.inputs.greaterThan); + const lessThanFps = Number(args.inputs.lessThan); + + const VideoFrameRate = args.inputFileObj?.meta?.VideoFrameRate; + + if (VideoFrameRate) { + if (VideoFrameRate >= greaterThanFps && VideoFrameRate <= lessThanFps) { + isWithinRange = true; + } + } else { + throw new Error('Video framerate not found'); + } + + if (isWithinRange) { + args.jobLog(`Video framerate of ${VideoFrameRate} is within range of ${greaterThanFps} and ${lessThanFps}`); + } else { + args.jobLog(`Video framerate of ${VideoFrameRate} is not within range of ${greaterThanFps} and ${lessThanFps}`); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/check10Bit/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/check10Bit/1.0.0/index.ts index 88ac7e9f9..04548c2c4 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/video/check10Bit/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/video/check10Bit/1.0.0/index.ts @@ -13,6 +13,8 @@ const details = (): IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], @@ -36,11 +38,21 @@ const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { let is10Bit = false; - 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.bits_per_raw_sample === 10) { - is10Bit = true; + if (Array.isArray(args?.inputFileObj?.ffProbeData?.streams)) { + for (let i = 0; i < args.inputFileObj.ffProbeData.streams.length; i += 1) { + const stream = args.inputFileObj.ffProbeData.streams[i]; + if ( + stream.codec_type === 'video' + && ( + stream.bits_per_raw_sample === 10 + || stream.pix_fmt === 'yuv420p10le' + ) + ) { + is10Bit = true; + } } + } else { + throw new Error('File has not stream data'); } return { diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/checkHdr/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/checkHdr/1.0.0/index.ts index ec9353d4e..f28f79a92 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/video/checkHdr/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/video/checkHdr/1.0.0/index.ts @@ -6,13 +6,15 @@ import { /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = (): IpluginDetails => ({ - name: 'Check HDR', + name: 'Check HDR Video', description: 'Check if video is HDR', style: { borderColor: 'orange', }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], @@ -36,16 +38,20 @@ const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { let isHdr = false; - 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.transfer_characteristics === 'smpte2084' - && stream.color_primaries === 'bt2020' - && stream.color_range === 'tv' - ) { - isHdr = true; + if (Array.isArray(args?.inputFileObj?.ffProbeData?.streams)) { + for (let i = 0; i < args.inputFileObj.ffProbeData.streams.length; i += 1) { + const stream = args.inputFileObj.ffProbeData.streams[i]; + if ( + stream.codec_type === 'video' + && stream.color_transfer === 'smpte2084' + && stream.color_primaries === 'bt2020' + && stream.color_range === 'tv' + ) { + isHdr = true; + } } + } else { + throw new Error('File has not stream data'); } return { diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/checkOverallBitrate/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/checkOverallBitrate/1.0.0/index.ts new file mode 100644 index 000000000..d9bd7ee26 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/video/checkOverallBitrate/1.0.0/index.ts @@ -0,0 +1,104 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Check Overall Bitrate', + description: 'Check if overall file bitrate is within a specific range', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'unit', + type: 'string', + defaultValue: 'kbps', + inputUI: { + type: 'dropdown', + options: [ + 'bps', + 'kbps', + 'mbps', + ], + }, + tooltip: 'Specify the unit to use', + }, + { + name: 'greaterThan', + type: 'number', + defaultValue: '0', + inputUI: { + type: 'text', + }, + tooltip: 'Specify lower bound', + }, + { + name: 'lessThan', + type: 'number', + defaultValue: '10000', + inputUI: { + type: 'text', + }, + tooltip: 'Specify upper bound', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File within range', + }, + { + number: 2, + tooltip: 'File not within range', + }, + ], +}); + +// 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); + + let isWithinRange = false; + + let greaterThanBits = Number(args.inputs.greaterThan); + let lessThanBits = Number(args.inputs.lessThan); + + if (args.inputs.unit === 'kbps') { + greaterThanBits *= 1000; + lessThanBits *= 1000; + } else if (args.inputs.unit === 'mbps') { + greaterThanBits *= 1000000; + lessThanBits *= 1000000; + } + + args.jobLog(`File bitrate is ${args.inputFileObj.bit_rate} bps`); + args.jobLog(`Checking if bitrate is within range ${greaterThanBits} bps and ${lessThanBits} bps`); + + if (args.inputFileObj.bit_rate >= greaterThanBits && args.inputFileObj.bit_rate <= lessThanBits) { + isWithinRange = true; + args.jobLog('File bitrate is within range'); + } else { + args.jobLog('File bitrate is not within range'); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.ts index 45c75d25c..4c6182d45 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.ts @@ -13,6 +13,8 @@ const details = (): IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ @@ -80,9 +82,15 @@ const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { lessThanBits *= 1000000; } + let hasVideoBitrate = false; + if (args.inputFileObj?.mediaInfo?.track) { args.inputFileObj.mediaInfo.track.forEach((stream) => { - if (stream['@type'] === 'video') { + if (stream['@type'].toLowerCase() === 'video') { + if (stream.BitRate) { + hasVideoBitrate = true; + args.jobLog(`Found video bitrate: ${stream.BitRate}`); + } if (stream.BitRate >= greaterThanBits && stream.BitRate <= lessThanBits) { isWithinRange = true; } @@ -90,6 +98,10 @@ const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { }); } + if (!hasVideoBitrate) { + throw new Error('Video bitrate not found'); + } + return { outputFileObj: args.inputFileObj, outputNumber: isWithinRange ? 1 : 2, diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.ts index 946ecf8ca..21b8c33fe 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.ts @@ -13,6 +13,8 @@ const details = ():IpluginDetails => ({ }, tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [ diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoResolution/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoResolution/1.0.0/index.ts index cc38a4e1b..0e76a04e9 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoResolution/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoResolution/1.0.0/index.ts @@ -14,6 +14,8 @@ const details = ():IpluginDetails => ({ tags: 'video', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: 'faQuestion', inputs: [], @@ -63,9 +65,39 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); + let outputNumber = 9; + switch (args.inputFileObj.video_resolution) { + case '480p': + outputNumber = 1; + break; + case '576p': + outputNumber = 2; + break; + case '720p': + outputNumber = 3; + break; + case '1080p': + outputNumber = 4; + break; + case '1440p': + outputNumber = 5; + break; + case '4KUHD': + outputNumber = 6; + break; + case 'DCI4K': + outputNumber = 7; + break; + case '8KUHD': + outputNumber = 8; + break; + default: + outputNumber = 9; + } + return { outputFileObj: args.inputFileObj, - outputNumber: 1, + outputNumber, variables: args.variables, }; }; diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/runHealthCheck/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/runHealthCheck/1.0.0/index.ts new file mode 100644 index 000000000..7a9d9969c --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/video/runHealthCheck/1.0.0/index.ts @@ -0,0 +1,113 @@ +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ + +import { CLI } from '../../../../FlowHelpers/1.0.0/cliUtils'; +import { getContainer, getFileName, getPluginWorkDir } from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint-disable no-param-reassign */ +const details = (): IpluginDetails => ({ + name: 'Run Health Check', + description: 'Run a quick health check using HandBrake or a thorough health check using FFmpeg', + style: { + borderColor: '#6efefc', + }, + tags: 'video', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'type', + type: 'string', + defaultValue: 'quick', + inputUI: { + type: 'dropdown', + options: [ + 'quick', + 'thorough', + ], + }, + tooltip: 'Specify the container to use', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = async (args:IpluginInputArgs):Promise => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + const type = String(args.inputs.type); + + args.jobLog(`Running health check of type ${type}`); + + const outputFilePath = `${getPluginWorkDir(args)}/${getFileName(args.inputFileObj._id)}` + + `.${getContainer(args.inputFileObj._id)}`; + + let cliPath = args.handbrakePath; + let cliArgs = [ + '-i', + args.inputFileObj._id, + '-o', + outputFilePath, + '--scan', + ]; + + if (type === 'thorough') { + cliPath = args.ffmpegPath; + + cliArgs = [ + '-stats', + '-v', + 'error', + '-i', + args.inputFileObj._id, + '-f', + 'null', + '-max_muxing_queue_size', + '9999', + outputFilePath, + ]; + } + + const cli = new CLI({ + cli: cliPath, + spawnArgs: cliArgs, + spawnOpts: {}, + jobLog: args.jobLog, + outputFilePath, + inputFileObj: args.inputFileObj, + logFullCliOutput: args.logFullCliOutput, + updateWorker: args.updateWorker, + }); + + const res = await cli.runCli(); + + if (res.cliExitCode !== 0) { + args.jobLog('Running CLI failed'); + throw new Error('Running CLI failed'); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/transcodeVideo/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/transcodeVideo/1.0.0/index.ts index 4fbfd447d..50f48b334 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/video/transcodeVideo/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/video/transcodeVideo/1.0.0/index.ts @@ -15,6 +15,8 @@ const details = ():IpluginDetails => ({ tags: '', isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', sidebarPosition: -1, icon: '', inputs: [ diff --git a/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter1.tsx b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter1.tsx new file mode 100644 index 000000000..b6425d811 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter1.tsx @@ -0,0 +1,112 @@ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ({ + "name": "Chapter 1: Getting Started", + "description": "Chapter 1: Getting Started", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "_YTuyCZg3", + "position": { + "x": 644.7725474007168, + "y": -59.78556037646227 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "RQzydYbay", + "position": { + "x": 644.8785689715966, + "y": 285.63446752627516 + } + }, + { + "name": "1. Hello and welcome to Tdarr! This is a comment plugin. It doesn't do anything except help explain what's going on in a flow! You can place them anywhere and even link them together.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "df5cejfZP", + "position": { + "x": 774.8672137292031, + "y": -254.93856109034408 + } + }, + { + "name": "2. See! This comment won't do anything. The file from the previous plugin will be passed straight to the next plugin", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "30CajwYP2", + "position": { + "x": 644.6915712919753, + "y": 135.90533672888392 + } + }, + { + "name": "3. This here is an input file plugin and it's where every flow starts. You can only have ONE of these per flow.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "734dA76hg", + "position": { + "x": 444.5704060029551, + "y": -3.4693570957774114 + } + }, + { + "name": "4. That's it for this one, see you in the next chapter!", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "rkYonbPgX", + "position": { + "x": 443.9627448274695, + "y": 332.6480632642012 + } + } + ], + "flowEdges": [ + { + "source": "_YTuyCZg3", + "sourceHandle": "1", + "target": "30CajwYP2", + "targetHandle": null, + "id": "HUBIf10ny" + }, + { + "source": "30CajwYP2", + "sourceHandle": "1", + "target": "RQzydYbay", + "targetHandle": null, + "id": "Gd19X19w1" + }, + { + "source": "df5cejfZP", + "sourceHandle": "1", + "target": "30CajwYP2", + "targetHandle": null, + "id": "0EA92XgvP" + }, + { + "source": "734dA76hg", + "sourceHandle": "1", + "target": "rkYonbPgX", + "targetHandle": null, + "id": "lXbYouTsz" + } + ] +}); + +export { + details, +}; diff --git a/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter2.tsx b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter2.tsx new file mode 100644 index 000000000..a43320df0 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter2.tsx @@ -0,0 +1,191 @@ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ({ + "name": "Chapter 2: The Basics", + "description": "Chapter 2: The Basics", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "p2KPpRjnB", + "position": { + "x": 414.1115477468154, + "y": -216.87055056329626 + } + }, + { + "name": "1. The flow follows the current 'working file' which we can run checks and take actions on.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "ecLynt2i0", + "position": { + "x": 197.4536903827362, + "y": -265.54506622009336 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "jUig7_cRU", + "position": { + "x": 439.02078192278447, + "y": 122.5624161723565 + } + }, + { + "name": "Rename File to have _BigFile", + "sourceRepo": "Community", + "pluginName": "renameFile", + "version": "1.0.0", + "inputsDB": { + "fileRename": "${fileName}_BigFile.${container}" + }, + "id": "2l0pB_oXW", + "position": { + "x": 257.94626475719076, + "y": -21.078426771503985 + } + }, + { + "name": "Check File Size", + "sourceRepo": "Community", + "pluginName": "checkFileSize", + "version": "1.0.0", + "inputsDB": { + "greaterThan": "1", + "lessThan": "10000" + }, + "id": "oDkceuMNL", + "position": { + "x": 413.7748155871969, + "y": -110.90469509295968 + } + }, + { + "name": "Each plugin can only have one input handle but many plugins can link to it. Plugins which only check something are typically orange coloured and have 2 or more outputs.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "HTvMe6FSV", + "position": { + "x": 34.402701566604065, + "y": -184.71873806260285 + } + }, + { + "name": "Once you make an action on a file, in almost all cases the output is the new file. It will be located in your library cache folder.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "3O3ECJdF-", + "position": { + "x": 33.1114649694174, + "y": 113.19141666640903 + } + }, + { + "name": "Typical usage is to replace the original file. So this plugin will replace the original file with the new file.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "qA8hT1mmP", + "position": { + "x": 355.2680532661178, + "y": 199.7482565776084 + } + }, + { + "name": "This flow route doesn't change the file, so the Replace Original File plugin won't do anything and the flow will end succesffully.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "1vBp13H02", + "position": { + "x": 597.7143477707415, + "y": -48.77347490679115 + } + }, + { + "name": "Double click on a plugin to see what each GREEN output does. Ignore the RED outputs for now.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "s-m8qOhJ7", + "position": { + "x": 34.4143205430116, + "y": -5.644569445757767 + } + } + ], + "flowEdges": [ + { + "source": "p2KPpRjnB", + "sourceHandle": "1", + "target": "oDkceuMNL", + "targetHandle": null, + "id": "S8inufSTF" + }, + { + "source": "oDkceuMNL", + "sourceHandle": "1", + "target": "2l0pB_oXW", + "targetHandle": null, + "id": "LFCRv0WUh" + }, + { + "source": "2l0pB_oXW", + "sourceHandle": "1", + "target": "jUig7_cRU", + "targetHandle": null, + "id": "w0K3dKylI" + }, + { + "source": "oDkceuMNL", + "sourceHandle": "2", + "target": "jUig7_cRU", + "targetHandle": null, + "id": "SNdz3urrJ" + }, + { + "source": "ecLynt2i0", + "sourceHandle": "1", + "target": "HTvMe6FSV", + "targetHandle": null, + "id": "7qPHR6V9P" + }, + { + "source": "3O3ECJdF-", + "sourceHandle": "1", + "target": "qA8hT1mmP", + "targetHandle": null, + "id": "GjDmOX_EI" + }, + { + "source": "HTvMe6FSV", + "sourceHandle": "1", + "target": "s-m8qOhJ7", + "targetHandle": null, + "id": "0bPlyyR9Q" + }, + { + "source": "s-m8qOhJ7", + "sourceHandle": "1", + "target": "3O3ECJdF-", + "targetHandle": null, + "id": "Mxxly19vC" + } + ] +}); + +export { + details, +}; diff --git a/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter3.tsx b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter3.tsx new file mode 100644 index 000000000..39ba7ff1f --- /dev/null +++ b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter3.tsx @@ -0,0 +1,227 @@ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ({ + "name": "Chapter 3: FFmpeg Command", + "description": "Chapter 3: FFmpeg Command", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 648.9333795070321, + "y": -12.529435106431094 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "QdLvoNjuG", + "position": { + "x": 723.9430232247286, + "y": 534.7914903208923 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "-kY9osnGE", + "position": { + "x": 399.6705241883612, + "y": 143.02276817432977 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "pmoPx8W0W", + "position": { + "x": 400.42838247161643, + "y": 438.58749864385743 + } + }, + { + "name": "Set Container", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetContainer", + "version": "1.0.0", + "id": "-DEIJA3Pf", + "position": { + "x": 401.1862407548717, + "y": 335.51877212115033 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "U0fVPXskr", + "position": { + "x": 400.1862407548716, + "y": 249.12292783005773 + } + }, + { + "name": "The FFmpeg Command plugins dynamically create an FFmpeg command depending on the input file", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "hGnpEHnk5", + "position": { + "x": 254.91444207269103, + "y": -44.61887485112061 + } + }, + { + "name": "You must always begin an FFmpeg command using the 'Begin Command' Plugin", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "saj94a135", + "position": { + "x": 201.288800916537, + "y": 100.94856498487928 + } + }, + { + "name": "In this example, if the video file is already in h265/hevc and mkv container, no action will be taken on the file. To force re-encoding, you can use the forceEncoding option on the Video Encoder plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "sb5MvVryc", + "position": { + "x": 201.61485276007585, + "y": 222.09640730256172 + } + }, + { + "name": "Once the FFmpeg command has been created, you need to execute it using this plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "V0QGN5PKA", + "position": { + "x": 202.61485276007582, + "y": 440.0964073025617 + } + }, + { + "name": "Once again, the output contains the new cache file (or the original file if no action was taken on the file). If there's a new cache file, the 'Replace Original File' plugin will replace the original file, else it will do nothing.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "i4eODNlBc", + "position": { + "x": 536.6148527600759, + "y": 568.0964073025617 + } + } + ], + "flowEdges": [ + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "ldiZljXp2" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "-kY9osnGE", + "sourceHandle": "1", + "target": "U0fVPXskr", + "targetHandle": null, + "id": "wuqNLcC1D" + }, + { + "source": "U0fVPXskr", + "sourceHandle": "1", + "target": "-DEIJA3Pf", + "targetHandle": null, + "id": "Coq5pIs3c" + }, + { + "source": "-DEIJA3Pf", + "sourceHandle": "1", + "target": "pmoPx8W0W", + "targetHandle": null, + "id": "fGjbMXOng" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "-kY9osnGE", + "targetHandle": null, + "id": "E5NHstmdF" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "k9JywvYcK" + }, + { + "source": "hGnpEHnk5", + "sourceHandle": "1", + "target": "saj94a135", + "targetHandle": null, + "id": "dX6DiWPJX" + }, + { + "source": "saj94a135", + "sourceHandle": "1", + "target": "sb5MvVryc", + "targetHandle": null, + "id": "0MAqJvu_e" + }, + { + "source": "sb5MvVryc", + "sourceHandle": "1", + "target": "V0QGN5PKA", + "targetHandle": null, + "id": "57NrKKG2n" + }, + { + "source": "V0QGN5PKA", + "sourceHandle": "1", + "target": "i4eODNlBc", + "targetHandle": null, + "id": "BHwljK8rj" + } + ] +}); + +export { + details, +}; diff --git a/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter4p1.tsx b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter4p1.tsx new file mode 100644 index 000000000..35eb93e27 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter4p1.tsx @@ -0,0 +1,216 @@ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ({ + "name": "Chapter 4: Flow Errors Part 1", + "description": "Chapter 4: Flow Errors Part 1", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 752.4065242952165, + "y": 51.12406033129332 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "QdLvoNjuG", + "position": { + "x": 773.1888091521793, + "y": 727.583503313465 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "-kY9osnGE", + "position": { + "x": 399.6705241883612, + "y": 143.02276817432977 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "pmoPx8W0W", + "position": { + "x": 416.1451226612283, + "y": 433.3485852473201 + } + }, + { + "name": "Set Container", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetContainer", + "version": "1.0.0", + "id": "-DEIJA3Pf", + "position": { + "x": 401.1862407548717, + "y": 335.51877212115033 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "U0fVPXskr", + "position": { + "x": 400.1862407548716, + "y": 249.12292783005773 + } + }, + { + "name": "If an unhandled error occurs during the flow, the flow will stop and the file will be moved to the Transcode: Error/Cancelled tab. You can then review the job report to see what went wrong.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "mNaOzfS0Y", + "position": { + "x": 604.5850500985517, + "y": 166.18413013606266 + } + }, + { + "name": "Fail Flow", + "sourceRepo": "Community", + "pluginName": "failFlow", + "version": "1.0.0", + "id": "mNwoZNlmo", + "position": { + "x": 616.8564543703576, + "y": 578.3209514237449 + } + }, + { + "name": "Compare File Size", + "sourceRepo": "Community", + "pluginName": "compareFileSize", + "version": "1.0.0", + "id": "YGd45fK8d", + "position": { + "x": 518.9335431151374, + "y": 502.8688871164036 + } + }, + { + "name": "You can also force a flow to fail which can be useful in certain situation such as here.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "9QkIvxxxx", + "position": { + "x": 678.3646507954192, + "y": 429.7476734555484 + } + } + ], + "flowEdges": [ + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "ldiZljXp2" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "-kY9osnGE", + "sourceHandle": "1", + "target": "U0fVPXskr", + "targetHandle": null, + "id": "wuqNLcC1D" + }, + { + "source": "U0fVPXskr", + "sourceHandle": "1", + "target": "-DEIJA3Pf", + "targetHandle": null, + "id": "Coq5pIs3c" + }, + { + "source": "-DEIJA3Pf", + "sourceHandle": "1", + "target": "pmoPx8W0W", + "targetHandle": null, + "id": "fGjbMXOng" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "-kY9osnGE", + "targetHandle": null, + "id": "E5NHstmdF" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "1", + "target": "YGd45fK8d", + "targetHandle": null, + "id": "bldP67hmm" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "fw9Le5zqo" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "2", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "wd7SmimpM" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "3", + "target": "mNwoZNlmo", + "targetHandle": null, + "id": "RmpqCf-Vh" + }, + { + "source": "mNaOzfS0Y", + "sourceHandle": "1", + "target": "9QkIvxxxx", + "targetHandle": null, + "id": "4Yez6rEN2" + } + ] +}); + +export { + details, +}; diff --git a/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter4p2.tsx b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter4p2.tsx new file mode 100644 index 000000000..2fbf8ddf0 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter4p2.tsx @@ -0,0 +1,310 @@ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ({ + "name": "Chapter 4: Flow Errors Part 2 - On Flow Error", + "description": "Chapter 4: Flow Errors Part 2 - On Flow Error", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 752.4065242952165, + "y": 51.12406033129332 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "QdLvoNjuG", + "position": { + "x": 773.1888091521793, + "y": 727.583503313465 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "-kY9osnGE", + "position": { + "x": 399.6705241883612, + "y": 143.02276817432977 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "pmoPx8W0W", + "position": { + "x": 416.1451226612283, + "y": 433.3485852473201 + } + }, + { + "name": "Set Container", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetContainer", + "version": "1.0.0", + "id": "-DEIJA3Pf", + "position": { + "x": 401.1862407548717, + "y": 335.51877212115033 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "U0fVPXskr", + "position": { + "x": 400.1862407548716, + "y": 249.12292783005773 + } + }, + { + "name": "Fail Flow", + "sourceRepo": "Community", + "pluginName": "failFlow", + "version": "1.0.0", + "id": "mNwoZNlmo", + "position": { + "x": 616.8564543703576, + "y": 578.3209514237449 + } + }, + { + "name": "Compare File Size", + "sourceRepo": "Community", + "pluginName": "compareFileSize", + "version": "1.0.0", + "id": "YGd45fK8d", + "position": { + "x": 518.9335431151374, + "y": 502.8688871164036 + } + }, + { + "name": "On Flow Error", + "sourceRepo": "Community", + "pluginName": "onFlowError", + "version": "1.0.0", + "id": "yMWso-uZa", + "position": { + "x": 922.4197900595414, + "y": 161.088098623682 + } + }, + { + "name": "1. To handle an error that occurs anywhere in this specifc flow, you can use the 'On Flow Error' plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "jpEn9FVQX", + "position": { + "x": 1018.5785464566798, + "y": 65.98583847747655 + } + }, + { + "name": "All unhandled errors and the 'Fail Flow' plugin IN THIS FLOW will trigger the 'On Flow Error' plugin IN THIS FLOW", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "vRFTPo0p5", + "position": { + "x": 698.3866844766682, + "y": 451.2472106052397 + } + }, + { + "name": "If another error occurs in the 'On Flow Error' flow then the flow will end and the file will be moved to the transcode 'Transcode: Error/Cancelled' tab. The 'On Flow Error' plugin will NOT be run again (to prevent infinite error loops)", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "fAQouEkEY", + "position": { + "x": 1183.421069816697, + "y": 228.19157008625297 + } + }, + { + "name": "Send Web Request", + "sourceRepo": "Community", + "pluginName": "webRequest", + "version": "1.0.0", + "id": "42P9lb0B3", + "position": { + "x": 897.7260729664589, + "y": 469.1243455181426 + } + }, + { + "name": "Even if all the plugins in the error flow complete successfully, the file will still be moved to the 'Transcode: Error/Cancelled' tab at the end.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "Ke1S57we6", + "position": { + "x": 1006.3188415680227, + "y": 541.3784972055968 + } + }, + { + "name": "Fail Flow", + "sourceRepo": "Community", + "pluginName": "failFlow", + "version": "1.0.0", + "id": "yj3grm5d8", + "position": { + "x": 1047.7090907308577, + "y": 394.7331214427515 + } + }, + { + "name": "Check File Exists", + "sourceRepo": "Community", + "pluginName": "checkFileExists", + "version": "1.0.0", + "id": "S_rVuKn8S", + "position": { + "x": 926.2665476107438, + "y": 277.7066707997331 + } + } + ], + "flowEdges": [ + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "ldiZljXp2" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "-kY9osnGE", + "sourceHandle": "1", + "target": "U0fVPXskr", + "targetHandle": null, + "id": "wuqNLcC1D" + }, + { + "source": "U0fVPXskr", + "sourceHandle": "1", + "target": "-DEIJA3Pf", + "targetHandle": null, + "id": "Coq5pIs3c" + }, + { + "source": "-DEIJA3Pf", + "sourceHandle": "1", + "target": "pmoPx8W0W", + "targetHandle": null, + "id": "fGjbMXOng" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "-kY9osnGE", + "targetHandle": null, + "id": "E5NHstmdF" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "1", + "target": "YGd45fK8d", + "targetHandle": null, + "id": "bldP67hmm" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "fw9Le5zqo" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "2", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "wd7SmimpM" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "3", + "target": "mNwoZNlmo", + "targetHandle": null, + "id": "RmpqCf-Vh" + }, + { + "source": "jpEn9FVQX", + "sourceHandle": "1", + "target": "fAQouEkEY", + "targetHandle": null, + "id": "5mv1ls7Ib" + }, + { + "source": "fAQouEkEY", + "sourceHandle": "1", + "target": "Ke1S57we6", + "targetHandle": null, + "id": "_VEvhMOtk" + }, + { + "source": "yMWso-uZa", + "sourceHandle": "1", + "target": "S_rVuKn8S", + "targetHandle": null, + "id": "yweCdlSWM" + }, + { + "source": "S_rVuKn8S", + "sourceHandle": "1", + "target": "yj3grm5d8", + "targetHandle": null, + "id": "xI3eh7wZp" + }, + { + "source": "S_rVuKn8S", + "sourceHandle": "2", + "target": "42P9lb0B3", + "targetHandle": null, + "id": "V-qf6QBC4" + } + ] +}); + +export { + details, +}; diff --git a/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter4p3.tsx b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter4p3.tsx new file mode 100644 index 000000000..dbf952929 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter4p3.tsx @@ -0,0 +1,319 @@ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ( + { + "name": "Chapter 4: Flow Errors Part 3 - Plugin-specific Error Handling", + "description": "Chapter 4: Flow Errors Part 3 - Plugin-specific Error Handling", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 541.9238836009351, + "y": 32.863009312154745 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "QdLvoNjuG", + "position": { + "x": 773.1888091521793, + "y": 727.583503313465 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "-kY9osnGE", + "position": { + "x": 399.6705241883612, + "y": 143.02276817432977 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "pmoPx8W0W", + "position": { + "x": 399.8062875388412, + "y": 417.9708580733087 + } + }, + { + "name": "Set Container", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetContainer", + "version": "1.0.0", + "id": "-DEIJA3Pf", + "position": { + "x": 401.1862407548717, + "y": 335.51877212115033 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "U0fVPXskr", + "position": { + "x": 400.1862407548716, + "y": 249.12292783005773 + } + }, + { + "name": "Fail Flow", + "sourceRepo": "Community", + "pluginName": "failFlow", + "version": "1.0.0", + "id": "mNwoZNlmo", + "position": { + "x": 616.8564543703576, + "y": 578.3209514237449 + } + }, + { + "name": "Compare File Size", + "sourceRepo": "Community", + "pluginName": "compareFileSize", + "version": "1.0.0", + "id": "YGd45fK8d", + "position": { + "x": 518.9335431151374, + "y": 502.8688871164036 + } + }, + { + "name": "On Flow Error", + "sourceRepo": "Community", + "pluginName": "onFlowError", + "version": "1.0.0", + "id": "yMWso-uZa", + "position": { + "x": 1060.1712089610803, + "y": 145.28055874973487 + } + }, + { + "name": "Send Web Request", + "sourceRepo": "Community", + "pluginName": "webRequest", + "version": "1.0.0", + "id": "Bc2bZtgBc", + "position": { + "x": 922.4677516191077, + "y": 279.351803016885 + } + }, + { + "name": "Tdarr also offers plugin-specific error handling using the RED connection on each plugin. The flow path will be triggered if an unhandled error occurs within that specific plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "H__edbTLw", + "position": { + "x": 767.7320724785685, + "y": -230.81328720265796 + } + }, + { + "name": "Send Web Request: Ping Melissa to check network storage", + "sourceRepo": "Community", + "pluginName": "webRequest", + "version": "1.0.0", + "id": "X9NEJCEgk", + "position": { + "x": 880.7836232681229, + "y": -39.676791653194755 + } + }, + { + "name": "Send Web Request: Ping Romesh to check transcode log", + "sourceRepo": "Community", + "pluginName": "webRequest", + "version": "1.0.0", + "id": "BO0c5TlKq", + "position": { + "x": 661.6510110384603, + "y": 300.5554220718082 + } + }, + { + "name": "This allows very specific error flows, for example pinging different team members for different errors.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "zTVwtbuxI", + "position": { + "x": 657.8440331872728, + "y": 166.67570418496692 + } + }, + { + "name": "The plugin-specifc error handling will NOT trigger the 'On Flow Error' plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "N9E-u8l0o", + "position": { + "x": 1170.7519669401188, + "y": 56.50523025141092 + } + }, + { + "name": "But you can still join the plugin-specific error handling flow onto the rest of the 'On Flow Error' Flow", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "AOi3vLobO", + "position": { + "x": 1125.9768689675689, + "y": 284.4868129833375 + } + } + ], + "flowEdges": [ + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "ldiZljXp2" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "-kY9osnGE", + "sourceHandle": "1", + "target": "U0fVPXskr", + "targetHandle": null, + "id": "wuqNLcC1D" + }, + { + "source": "U0fVPXskr", + "sourceHandle": "1", + "target": "-DEIJA3Pf", + "targetHandle": null, + "id": "Coq5pIs3c" + }, + { + "source": "-DEIJA3Pf", + "sourceHandle": "1", + "target": "pmoPx8W0W", + "targetHandle": null, + "id": "fGjbMXOng" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "-kY9osnGE", + "targetHandle": null, + "id": "E5NHstmdF" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "1", + "target": "YGd45fK8d", + "targetHandle": null, + "id": "bldP67hmm" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "1", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "fw9Le5zqo" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "2", + "target": "QdLvoNjuG", + "targetHandle": null, + "id": "wd7SmimpM" + }, + { + "source": "YGd45fK8d", + "sourceHandle": "3", + "target": "mNwoZNlmo", + "targetHandle": null, + "id": "RmpqCf-Vh" + }, + { + "source": "yMWso-uZa", + "sourceHandle": "1", + "target": "Bc2bZtgBc", + "targetHandle": null, + "id": "7k8P1VYv6" + }, + { + "source": "gtZCtmY-l", + "sourceHandle": "err1", + "target": "X9NEJCEgk", + "targetHandle": null, + "id": "9rhuR5eSI" + }, + { + "source": "pmoPx8W0W", + "sourceHandle": "err1", + "target": "BO0c5TlKq", + "targetHandle": null, + "id": "ttZgLtKF3" + }, + { + "source": "H__edbTLw", + "sourceHandle": "1", + "target": "zTVwtbuxI", + "targetHandle": null, + "id": "5sjNNMXAK" + }, + { + "source": "X9NEJCEgk", + "sourceHandle": "1", + "target": "Bc2bZtgBc", + "targetHandle": null, + "id": "OW-yqRQH5" + }, + { + "source": "N9E-u8l0o", + "sourceHandle": "1", + "target": "AOi3vLobO", + "targetHandle": null, + "id": "K440_LQm_" + } + ] + } +); + +export { + details, +}; diff --git a/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter5.tsx b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter5.tsx new file mode 100644 index 000000000..4016acd0f --- /dev/null +++ b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter5.tsx @@ -0,0 +1,159 @@ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ({ + "name": "Chapter 5: Go To Flow", + "description": "Chapter 5: Go To Flow", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 752.4065242952165, + "y": 51.12406033129332 + } + }, + { + "name": "On Flow Error", + "sourceRepo": "Community", + "pluginName": "onFlowError", + "version": "1.0.0", + "id": "yMWso-uZa", + "position": { + "x": 1122.33024332169, + "y": 226.4434391132305 + } + }, + { + "name": "You can use the Go To Flow to go to a different flow. The working file will be passed to that flow and will continue as normal. Double click on the plugin to select the flow you'd like to go to.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "rHV28Kbkv", + "position": { + "x": 462.0014512264263, + "y": 65.78412788449464 + } + }, + { + "name": "Go To Flow", + "sourceRepo": "Community", + "pluginName": "goToFlow", + "version": "1.0.0", + "id": "gOrbropah", + "position": { + "x": 572.7308895655424, + "y": 234.58707695358294 + } + }, + { + "name": "By design, if an error happens in a different flow, this 'On Flow Error' will not be called. Across all flows, the 'On Flow Error' plugin will only be called ONCE in the flow that the FIRST error occurred in. 'On Flow Error' plugins in flows before or after the current flow will not be called, even if an error occurs in them at a later time.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "7azuiVML9", + "position": { + "x": 1310.2464269220861, + "y": 134.70796523124582 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "ELn0kcc-1", + "position": { + "x": 778.4079905179452, + "y": 428.7308825254772 + } + }, + { + "name": "Go To Flow", + "sourceRepo": "Community", + "pluginName": "goToFlow", + "version": "1.0.0", + "id": "j5dOGi9zz", + "position": { + "x": 1122.484636036451, + "y": 397.97542817745443 + } + }, + { + "name": "After an error has occured you can even go to a different flow! So you can create a dedicated Error flow and go to it each time an error occurs within any of your flows! Useful for notifications etc.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "EpMxr2UuE", + "position": { + "x": 1205.7688307851763, + "y": 479.4625484620842 + } + } + ], + "flowEdges": [ + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "gOrbropah", + "targetHandle": null, + "id": "qVWE7SWt2" + }, + { + "source": "rHV28Kbkv", + "sourceHandle": "1", + "target": "7azuiVML9", + "targetHandle": null, + "id": "i0OUf3hAM" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "ELn0kcc-1", + "targetHandle": null, + "id": "hTaDcPw24" + }, + { + "source": "yMWso-uZa", + "sourceHandle": "1", + "target": "j5dOGi9zz", + "targetHandle": null, + "id": "6Hrh7vbfW" + }, + { + "source": "7azuiVML9", + "sourceHandle": "1", + "target": "EpMxr2UuE", + "targetHandle": null, + "id": "S_36mCXnL" + } + ] +}); + +export { + details, +}; diff --git a/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter6.tsx b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter6.tsx new file mode 100644 index 000000000..591dff578 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowTemplates/tutorials/chapter6.tsx @@ -0,0 +1,270 @@ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ({ + "name": "Chapter 6: The Review System", + "description": "Chapter 6: The Review System", + "tags": "", + "flowPlugins": [ + { + "name": "Input File", + "sourceRepo": "Community", + "pluginName": "inputFile", + "version": "1.0.0", + "id": "gtZCtmY-l", + "position": { + "x": 648.6536861377089, + "y": -82.45578042880155 + } + }, + { + "name": "Check Video Codec", + "sourceRepo": "Community", + "pluginName": "checkVideoCodec", + "version": "1.0.0", + "id": "PpLF-5jxp", + "position": { + "x": 752.4065242952165, + "y": 51.12406033129332 + } + }, + { + "name": "Replace Original File", + "sourceRepo": "Community", + "pluginName": "replaceOriginalFile", + "version": "1.0.0", + "id": "R0gX9B20d", + "position": { + "x": 879.7236115475249, + "y": 934.782797377857 + } + }, + { + "name": "Begin Command", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandStart", + "version": "1.0.0", + "id": "U6N3AQubH", + "position": { + "x": 546.8854528742303, + "y": 174.54090453410515 + } + }, + { + "name": "Execute", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandExecute", + "version": "1.0.0", + "id": "Lv-zb-iTw", + "position": { + "x": 543.172691292081, + "y": 368.6158072160807 + } + }, + { + "name": "Set Video Encoder", + "sourceRepo": "Community", + "pluginName": "ffmpegCommandSetVideoEncoder", + "version": "1.0.0", + "id": "1pOFUCuQR", + "position": { + "x": 545.0642491154337, + "y": 274.70711126791645 + } + }, + { + "name": "Require Review", + "sourceRepo": "Community", + "pluginName": "requireReview", + "version": "1.0.0", + "id": "oHpu2fZOi", + "position": { + "x": 631.7959812272709, + "y": 459.542392214296 + } + }, + { + "name": "Run Classic Transcode Plugin: Add Audio Stream", + "sourceRepo": "Community", + "pluginName": "runClassicTranscodePlugin", + "version": "1.0.0", + "inputsDB": { + "pluginSourceId": "Community:Tdarr_Plugin_00td_action_add_audio_stream_codec" + }, + "id": "RZX5jIP5I", + "position": { + "x": 632.2402074212371, + "y": 545.8356807635909 + } + }, + { + "name": "Run Classic Transcode Plugin", + "sourceRepo": "Community", + "pluginName": "runClassicTranscodePlugin", + "version": "1.0.0", + "inputsDB": { + "pluginSourceId": "Community:Tdarr_Plugin_00td_action_remove_audio_by_channel_count", + "channelCounts": "8" + }, + "id": "3zj5puRQ1", + "position": { + "x": 696.5390735282473, + "y": 758.7339551203235 + } + }, + { + "name": "Require Review", + "sourceRepo": "Community", + "pluginName": "requireReview", + "version": "1.0.0", + "id": "q8Pz_3HGh", + "position": { + "x": 634.7388490591334, + "y": 648.8668893974542 + } + }, + { + "name": "You can pause a flow by using the 'Require Review' plugin. This will cause the file to stay in the staging section on the Tdarr tab until the 'Reviewed' button is pressed. This allows you to check the last completed cache file.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "YPyMAbZ76", + "position": { + "x": 856.2716462414401, + "y": 343.06736953610425 + } + }, + { + "name": "Once the file has been reviewed, the flow will continue from the next plugin.", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "REt4UEEGD", + "position": { + "x": 856.7260936183322, + "y": 531.8099095812979 + } + }, + { + "name": "You can 'Require Review' as much as you like!", + "sourceRepo": "Community", + "pluginName": "comment", + "version": "1.0.0", + "id": "UhuSLjA8g", + "position": { + "x": 857.7070495622867, + "y": 709.362935437006 + } + }, + { + "name": "Require Review", + "sourceRepo": "Community", + "pluginName": "requireReview", + "version": "1.0.0", + "id": "8IU0bhEJs", + "position": { + "x": 780.2115299899054, + "y": 844.7348557026837 + } + } + ], + "flowEdges": [ + { + "source": "gtZCtmY-l", + "sourceHandle": "1", + "target": "PpLF-5jxp", + "targetHandle": null, + "id": "Cs5aBSUks" + }, + { + "source": "U6N3AQubH", + "sourceHandle": "1", + "target": "1pOFUCuQR", + "targetHandle": null, + "id": "RdnvWmv0o" + }, + { + "source": "1pOFUCuQR", + "sourceHandle": "1", + "target": "Lv-zb-iTw", + "targetHandle": null, + "id": "p-VkIS6DK" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "2", + "target": "U6N3AQubH", + "targetHandle": null, + "id": "x_vWzShYB" + }, + { + "source": "PpLF-5jxp", + "sourceHandle": "1", + "target": "R0gX9B20d", + "targetHandle": null, + "id": "CtIsUppTB" + }, + { + "source": "Lv-zb-iTw", + "sourceHandle": "1", + "target": "oHpu2fZOi", + "targetHandle": null, + "id": "d9tDIjd1L" + }, + { + "source": "oHpu2fZOi", + "sourceHandle": "1", + "target": "RZX5jIP5I", + "targetHandle": null, + "id": "kFP4WRftx" + }, + { + "source": "RZX5jIP5I", + "sourceHandle": "1", + "target": "q8Pz_3HGh", + "targetHandle": null, + "id": "nqbQJ9wUz" + }, + { + "source": "q8Pz_3HGh", + "sourceHandle": "1", + "target": "3zj5puRQ1", + "targetHandle": null, + "id": "Vx60urLP7" + }, + { + "source": "YPyMAbZ76", + "sourceHandle": "1", + "target": "REt4UEEGD", + "targetHandle": null, + "id": "B85VWeRRu" + }, + { + "source": "REt4UEEGD", + "sourceHandle": "1", + "target": "UhuSLjA8g", + "targetHandle": null, + "id": "wk44u1THD" + }, + { + "source": "3zj5puRQ1", + "sourceHandle": "1", + "target": "8IU0bhEJs", + "targetHandle": null, + "id": "9Q4vfDFmI" + }, + { + "source": "8IU0bhEJs", + "sourceHandle": "1", + "target": "R0gX9B20d", + "targetHandle": null, + "id": "8wXVHwiDC" + } + ] +}); + +export { + details, +}; diff --git a/FlowPluginsTs/CommunityFlowTemplates/video/basicVideo.ts b/FlowPluginsTs/CommunityFlowTemplates/video/basicVideo.ts index 9d65dc743..3a146d04e 100644 --- a/FlowPluginsTs/CommunityFlowTemplates/video/basicVideo.ts +++ b/FlowPluginsTs/CommunityFlowTemplates/video/basicVideo.ts @@ -1,7 +1,12 @@ -const details = () => ({ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ({ name: 'Basic HEVC Video Flow', description: 'Basic HEVC Video Flow', - tags: 'video', + tags: '', flowPlugins: [ { name: 'Input File', @@ -88,8 +93,6 @@ const details = () => ({ target: '91b7IrsEc', targetHandle: null, id: 'HhF4rw2DZ', - animated: true, - type: 'smoothstep', }, { source: '91b7IrsEc', @@ -97,8 +100,6 @@ const details = () => ({ target: '4Swd6qzvc', targetHandle: null, id: 'jJizyFUcr', - animated: true, - type: 'smoothstep', }, { source: '4Swd6qzvc', @@ -106,8 +107,6 @@ const details = () => ({ target: '8B_6pRd_U', targetHandle: null, id: '3Df7Xoy93', - animated: true, - type: 'smoothstep', }, { source: '450g167D8', @@ -115,8 +114,6 @@ const details = () => ({ target: '4fkfOyR3l', targetHandle: null, id: 'rE5Dsh9KM', - animated: true, - type: 'smoothstep', }, { source: '91b7IrsEc', @@ -124,8 +121,6 @@ const details = () => ({ target: '4fkfOyR3l', targetHandle: null, id: 'W2nVG7ts5', - animated: true, - type: 'smoothstep', }, { source: '8B_6pRd_U', @@ -133,8 +128,6 @@ const details = () => ({ target: 'TtKXi3Q7h', targetHandle: null, id: 'epqtLsPuG', - animated: true, - type: 'smoothstep', }, { source: 'TtKXi3Q7h', @@ -142,10 +135,10 @@ const details = () => ({ target: '450g167D8', targetHandle: null, id: 'ljOeP0cAZ', - animated: true, - type: 'smoothstep', }, ], }); -module.exports.details = details; +export { + details, +}; diff --git a/FlowPluginsTs/CommunityFlowTemplates/video/lowResolutionCopies.ts b/FlowPluginsTs/CommunityFlowTemplates/video/lowResolutionCopies.ts new file mode 100644 index 000000000..d9ddbcf8c --- /dev/null +++ b/FlowPluginsTs/CommunityFlowTemplates/video/lowResolutionCopies.ts @@ -0,0 +1,365 @@ +/* eslint-disable no-template-curly-in-string */ +/* eslint-disable import/prefer-default-export */ + +import { IflowTemplate } from '../../FlowHelpers/1.0.0/interfaces/interfaces'; + +const details = () :IflowTemplate => ({ + name: 'Create Low Resolution Video Copies', + description: 'Create Low Resolution Video Copies', + tags: '', + flowPlugins: [ + { + name: 'Input File', + sourceRepo: 'Community', + pluginName: 'inputFile', + version: '1.0.0', + id: 'pE6rU7gkW', + position: { + x: 764.3859715446088, + y: 54.59674430707997, + }, + }, + { + name: 'Check File Exists _480p', + sourceRepo: 'Community', + pluginName: 'checkFileExists', + version: '1.0.0', + inputsDB: { + fileToCheck: '${fileName}_480p.${container}', + }, + id: 'VyNRD3YjM', + position: { + x: 1127.8807371830678, + y: -1.4370146635981769, + }, + }, + { + name: 'Rename File _480p', + sourceRepo: 'Community', + pluginName: 'renameFile', + version: '1.0.0', + inputsDB: { + fileRename: '${fileName}_480p.${container}', + }, + id: 'VpCD-7LZJ', + position: { + x: 1398.163993949301, + y: 562.3533349776774, + }, + }, + { + name: 'Replace Original File', + sourceRepo: 'Community', + pluginName: 'replaceOriginalFile', + version: '1.0.0', + id: '1pj9oSg5G', + position: { + x: 736.3406162570204, + y: 598.8673432638388, + }, + }, + { + name: 'Check File Exists _720p', + sourceRepo: 'Community', + pluginName: 'checkFileExists', + version: '1.0.0', + inputsDB: { + fileToCheck: '${fileName}_720p.${container}', + }, + id: 'uDC6XT1Jy', + position: { + x: 1060.0100333142968, + y: 110.8981370311281, + }, + }, + { + name: 'Check File Name Includes', + sourceRepo: 'Community', + pluginName: 'checkFileNameIncludes', + version: '1.0.0', + inputsDB: { + terms: '_720p,_480p', + }, + id: 'wRipuaq4G', + position: { + x: 763.9976994431687, + y: 198.97576654117708, + }, + }, + { + name: 'Rename File _720p', + sourceRepo: 'Community', + pluginName: 'renameFile', + version: '1.0.0', + inputsDB: { + fileRename: '${fileName}_720p.${container}', + }, + id: 'cTKbaB8nT', + position: { + x: 1087.883110780845, + y: 509.6274273776863, + }, + }, + { + name: 'Move To Original Directory', + sourceRepo: 'Community', + pluginName: 'moveToOriginalDirectory', + version: '1.0.0', + id: 'mFRK-Z9WC', + position: { + x: 1162.0438576735944, + y: 608.0524996697887, + }, + }, + { + name: 'Set Original File', + sourceRepo: 'Community', + pluginName: 'setOriginalFile', + version: '1.0.0', + id: 'oD4u5PY9T', + position: { + x: 1020.6746394849897, + y: 738.9349550904227, + }, + }, + { + name: 'Begin Command', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandStart', + version: '1.0.0', + id: 'FSG9AOX5c', + position: { + x: 1171.2902386661297, + y: 178.0193821518036, + }, + }, + { + name: 'Set Video Encoder', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVideoEncoder', + version: '1.0.0', + inputsDB: { + forceEncoding: 'true', + }, + id: 'wcmBN2N02', + position: { + x: 1171.0819612214827, + y: 257.19366435734827, + }, + }, + { + name: 'Execute', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandExecute', + version: '1.0.0', + id: 'tmUd79-Fb', + position: { + x: 1167.5698309351776, + y: 406.2043896501846, + }, + }, + { + name: 'Begin Command', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandStart', + version: '1.0.0', + id: 'Jn6dcKd3i', + position: { + x: 1395.4614497255334, + y: 111.74717420966138, + }, + }, + { + name: 'Execute', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandExecute', + version: '1.0.0', + id: 'gbY0xIJnB', + position: { + x: 1398.0103706416776, + y: 417.6787803779547, + }, + }, + { + name: 'Set Video Resolution 480p', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVdeoResolution', + version: '1.0.0', + inputsDB: { + targetResolution: '480p', + }, + id: 'dzFEwECXB', + position: { + x: 1396.1961096759603, + y: 309.9727302535869, + }, + }, + { + name: 'Set Video Encoder', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVideoEncoder', + version: '1.0.0', + inputsDB: { + forceEncoding: 'true', + }, + id: '_EynbvgSl', + position: { + x: 1396.1961096759603, + y: 214.35898180146438, + }, + }, + { + name: 'Set Video Resolution 720p', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVdeoResolution', + version: '1.0.0', + inputsDB: { + targetResolution: '720p', + }, + id: 'CMm7MlE7g', + position: { + x: 1169.6624226114702, + y: 336.82482287402803, + }, + }, + ], + flowEdges: [ + { + source: 'pE6rU7gkW', + sourceHandle: '1', + target: 'wRipuaq4G', + targetHandle: null, + id: 'IE_oGhETB', + }, + { + source: 'wRipuaq4G', + sourceHandle: '1', + target: '1pj9oSg5G', + targetHandle: null, + id: 'QR6uGNUhE', + }, + { + source: 'wRipuaq4G', + sourceHandle: '2', + target: 'VyNRD3YjM', + targetHandle: null, + id: 'sh_kstv0D', + }, + { + source: 'uDC6XT1Jy', + sourceHandle: '1', + target: '1pj9oSg5G', + targetHandle: null, + id: 'G5jl85ijr', + }, + { + source: 'VyNRD3YjM', + sourceHandle: '1', + target: 'uDC6XT1Jy', + targetHandle: null, + id: 'DmUL9DS8q', + }, + { + source: 'VpCD-7LZJ', + sourceHandle: '1', + target: 'mFRK-Z9WC', + targetHandle: null, + id: 'ap4YXAxy3', + }, + { + source: 'cTKbaB8nT', + sourceHandle: '1', + target: 'mFRK-Z9WC', + targetHandle: null, + id: 'i9fr5J5pL', + }, + { + source: 'mFRK-Z9WC', + sourceHandle: '1', + target: 'oD4u5PY9T', + targetHandle: null, + id: 'KUw59S_Zl', + }, + { + source: 'oD4u5PY9T', + sourceHandle: '1', + target: 'wRipuaq4G', + targetHandle: null, + id: 'HlM4E6eV8', + }, + { + source: 'tmUd79-Fb', + sourceHandle: '1', + target: 'cTKbaB8nT', + targetHandle: null, + id: 'iJLmmoDLp', + }, + { + source: 'uDC6XT1Jy', + sourceHandle: '2', + target: 'FSG9AOX5c', + targetHandle: null, + id: 'iRTrU8utq', + }, + { + source: 'dzFEwECXB', + sourceHandle: '1', + target: 'gbY0xIJnB', + targetHandle: null, + id: 'A5cyCu_kx', + }, + { + source: 'Jn6dcKd3i', + sourceHandle: '1', + target: '_EynbvgSl', + targetHandle: null, + id: '1HajidLz-', + }, + { + source: '_EynbvgSl', + sourceHandle: '1', + target: 'dzFEwECXB', + targetHandle: null, + id: 'vEESYeSsL', + }, + { + source: 'VyNRD3YjM', + sourceHandle: '2', + target: 'Jn6dcKd3i', + targetHandle: null, + id: 'q8zd_qCSU', + }, + { + source: 'gbY0xIJnB', + sourceHandle: '1', + target: 'VpCD-7LZJ', + targetHandle: null, + id: 'leYMQdxHw', + }, + { + source: 'FSG9AOX5c', + sourceHandle: '1', + target: 'wcmBN2N02', + targetHandle: null, + id: 'Dl5MCSqQM', + }, + { + source: 'wcmBN2N02', + sourceHandle: '1', + target: 'CMm7MlE7g', + targetHandle: null, + id: 'GIpbjomC8', + }, + { + source: 'CMm7MlE7g', + sourceHandle: '1', + target: 'tmUd79-Fb', + targetHandle: null, + id: 'AxR9R10MY', + }, + ], +}); + +export { + details, +}; diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/classicPlugins.ts b/FlowPluginsTs/FlowHelpers/1.0.0/classicPlugins.ts new file mode 100644 index 000000000..00c89773e --- /dev/null +++ b/FlowPluginsTs/FlowHelpers/1.0.0/classicPlugins.ts @@ -0,0 +1,147 @@ +import { promises as fs } from 'fs'; +import { + getContainer, getFileName, getPluginWorkDir, getScanTypes, +} from './fileUtils'; +import { IpluginInputArgs } from './interfaces/interfaces'; + +export interface IrunClassicPlugin { + result:{ + processFile: boolean, + handBrakeMode?: boolean, + handbrakeMode?: boolean, + FFmpegMode?: boolean, + ffmpegMode?: boolean, + cliToUse?: string, + custom?: { + cliPath?: string, + args: string[], + outputPath: string, + }, + workerLog?: string, + transcodeSettingsLog?: string, + error?: string, + container: string, + preset: string, + }; + cacheFilePath:string; + absolutePath:string; +} + +export const runClassicPlugin = async (args:IpluginInputArgs, type:'filter'|'transcode'):Promise => { + const path = require('path'); + + const pluginSourceId = String(args.inputs.pluginSourceId); + const parts = pluginSourceId.split(':'); + const pluginSource = parts[0]; + const pluginId = parts[1]; + + const relativePluginPath = `../../../${pluginSource}/${pluginId}.js`; + const absolutePath = path.resolve(__dirname, relativePluginPath); + + let classicPlugin; + let pluginSrcStr = ''; + if (pluginSource === 'Community') { + classicPlugin = args.deps.importFresh(relativePluginPath); + pluginSrcStr = await fs.readFile(absolutePath, 'utf8'); + } else { + // eslint-disable-next-line no-await-in-loop + const res = await args.deps.axiosMiddleware('api/v2/read-plugin', { + plugin: { + id: pluginId, + source: pluginSource, + }, + }); + + classicPlugin = args.deps.requireFromString(res.pluginRaw, absolutePath); + pluginSrcStr = res.pluginRaw; + } + + if (type === 'filter' && classicPlugin.details().Operation !== 'Filter') { + throw new Error( + `${'This plugin is meant for classic plugins that have ' + + 'Operation: Filter. This classic plugin has Operation: '}${classicPlugin.details().Operation}` + + '. Please use the Run Classic Transcode Flow Plugin plugin instead.' + , + ); + } + + if (type !== 'filter' && classicPlugin.details().Operation === 'Filter') { + throw new Error( + `${'This plugin is meant for classic plugins that have ' + + 'Operation: Transcode. This classic plugin has Operation: '}${classicPlugin.details().Operation}` + + 'Please use the Run Classic Filter Flow Plugin plugin instead.' + , + ); + } + + if (Array.isArray(classicPlugin.dependencies)) { + if (args.installClassicPluginDeps) { + args.jobLog(`Installing dependencies for ${pluginSourceId}`); + await args.installClassicPluginDeps(classicPlugin.dependencies); + } else { + args.jobLog(`Not installing dependencies for ${pluginSourceId}, please update Tdarr`); + } + } else { + args.jobLog(`No depedencies to install for ${pluginSourceId}`); + } + + const container = getContainer(args.inputFileObj._id); + const cacheFilePath = `${getPluginWorkDir(args)}/${getFileName(args.inputFileObj._id)}.${container}`; + + const scanTypes = getScanTypes([pluginSrcStr]); + + const pluginInputFileObj = await args.deps.axiosMiddleware('api/v2/scan-individual-file', { + file: { + _id: args.inputFileObj._id, + file: args.inputFileObj.file, + DB: args.inputFileObj.DB, + footprintId: args.inputFileObj.footprintId, + }, + scanTypes, + }); + + const originalLibraryFile = await args.deps.axiosMiddleware('api/v2/scan-individual-file', { + file: { + _id: args.originalLibraryFile._id, + file: args.originalLibraryFile.file, + DB: args.originalLibraryFile.DB, + footprintId: args.originalLibraryFile.footprintId, + }, + scanTypes, + }); + + const otherArguments = { + handbrakePath: args.handbrakePath, + ffmpegPath: args.ffmpegPath, + mkvpropeditPath: args.mkvpropeditPath, + originalLibraryFile, + nodeHardwareType: args.nodeHardwareType, + pluginCycle: 0, + workerType: args.workerType, + version: args.config.version, + platform_arch_isdocker: args.platform_arch_isdocker, + cacheFilePath, + job: args.job, + }; + + const result = await classicPlugin.plugin( + pluginInputFileObj, + args.librarySettings, + args.inputs, + otherArguments, + ); + + if (result?.file?._id && args.inputFileObj._id !== result.file._id) { + // eslint-disable-next-line no-param-reassign + args.inputFileObj._id = result.file._id; + // eslint-disable-next-line no-param-reassign + args.inputFileObj.file = result.file.file; + args.jobLog(`File ID changed from ${args.inputFileObj._id} to ${result.file._id}`); + } + + return { + result, + cacheFilePath, + absolutePath, + }; +}; diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts b/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts index d0d615cf2..018f9a768 100644 --- a/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts +++ b/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts @@ -14,6 +14,12 @@ export const getFileName = (filePath: string): string => { return parts2.join('.'); }; +export const getFileAbosluteDir = (filePath: string):string => { + const parts = filePath.split('/'); + parts.pop(); + return parts.join('/'); +}; + export const getFfType = (codecType: string): string => (codecType === 'video' ? 'v' : 'a'); export const getSubStem = ({ @@ -92,3 +98,56 @@ export const moveFileAndValidate = async ({ } } }; + +export const getPluginWorkDir = (args: IpluginInputArgs):string => { + const pluginWorkDir = `${args.workDir}/${new Date().getTime()}`; + args.deps.fsextra.ensureDirSync(pluginWorkDir); + return pluginWorkDir; +}; + +export interface IscanTypes { + mediaInfoScan: boolean, + exifToolScan: boolean, + closedCaptionScan: boolean, + [index: string]: boolean, +} + +export const getScanTypes = (pluginsTextRaw: string[]): IscanTypes => { + const scanTypes: IscanTypes = { + exifToolScan: true, + mediaInfoScan: false, + closedCaptionScan: false, + }; + const scannerTypes = [ + // needed for frame and duration data for ffmpeg + // { + // type: 'exifToolScan', + // terms: [ + // 'meta', + // ], + // }, + { + type: 'mediaInfoScan', + terms: [ + 'mediaInfo', + ], + }, + { + type: 'closedCaptionScan', + terms: [ + 'hasClosedCaptions', + ], + }, + ]; + + const text = pluginsTextRaw.join(''); + + scannerTypes.forEach((scanner) => { + scanner.terms.forEach((term) => { + if (text.includes(term)) { + scanTypes[scanner.type] = true; + } + }); + }); + return scanTypes; +}; diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.test.ts b/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.test.ts index f1cfa1cf8..5f5a90bd4 100644 --- a/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.test.ts +++ b/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.test.ts @@ -4,6 +4,7 @@ const run = async () => { const encoderProperties = await getEncoder({ targetCodec: 'h264', hardwareEncoding: true, + hardwareType: 'auto', // @ts-expect-error type args: { workerType: 'transcodegpu', diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.ts b/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.ts index 47cc3b188..fe00c97bb 100644 --- a/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.ts +++ b/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.ts @@ -5,11 +5,13 @@ export const hasEncoder = async ({ encoder, inputArgs, filter, + args, }: { ffmpegPath: string, encoder: string, inputArgs: string[], filter: string, + args: IpluginInputArgs, }): Promise => { const { exec } = require('child_process'); let isEnabled = false; @@ -18,6 +20,10 @@ export const hasEncoder = async ({ const command = `${ffmpegPath} ${inputArgs.join(' ') || ''} -f lavfi -i color=c=black:s=256x256:d=1:r=30` + ` ${filter || ''}` + ` -c:v ${encoder} -f null /dev/null`; + + args.jobLog(`Checking for encoder ${encoder} with command:`); + args.jobLog(command); + exec(command, ( // eslint-disable-next-line error: any, @@ -31,6 +37,8 @@ export const hasEncoder = async ({ resolve(true); }); }); + + args.jobLog(`Encoder ${encoder} is ${isEnabled ? 'enabled' : 'disabled'}`); } catch (err) { // eslint-disable-next-line no-console console.log(err); @@ -112,34 +120,41 @@ export const getBestNvencDevice = ({ return nvencDevice; }; -const encoderFilter = (encoder:string, targetCodec:string) => { +const encoderFilter = (encoder: string, targetCodec: string) => { if (targetCodec === 'hevc' && (encoder.includes('hevc') || encoder.includes('h265'))) { return true; } if (targetCodec === 'h264' && encoder.includes('h264')) { return true; + } if (targetCodec === 'av1' && encoder.includes('av1')) { + return true; } return false; }; +export interface IgetEncoder { + encoder: string, + inputArgs: string[], + outputArgs: string[], + isGpu: boolean, + enabledDevices: IgpuEncoder[], +} + export const getEncoder = async ({ targetCodec, hardwareEncoding, + hardwareType, args, }: { targetCodec: string, hardwareEncoding: boolean, + hardwareType: string, args: IpluginInputArgs, -}): Promise<{ - encoder: string, - inputArgs: string[], - outputArgs: string[], - isGpu: boolean, -}> => { +}): Promise => { if ( args.workerType && args.workerType.includes('gpu') - && hardwareEncoding && (targetCodec === 'hevc' || targetCodec === 'h264')) { + && hardwareEncoding && (['hevc', 'h264', 'av1'].includes(targetCodec))) { const gpuEncoders: IgpuEncoder[] = [ { encoder: 'hevc_nvenc', @@ -158,6 +173,16 @@ export const getEncoder = async ({ outputArgs: [], filter: '', }, + { + encoder: 'hevc_qsv', + enabled: false, + inputArgs: [ + '-hwaccel', + 'qsv', + ], + outputArgs: [], + filter: '', + }, { encoder: 'hevc_vaapi', inputArgs: [ @@ -172,16 +197,6 @@ export const getEncoder = async ({ enabled: false, filter: '-vf format=nv12,hwupload', }, - { - encoder: 'hevc_qsv', - enabled: false, - inputArgs: [ - '-hwaccel', - 'qsv', - ], - outputArgs: [], - filter: '', - }, { encoder: 'hevc_videotoolbox', enabled: false, @@ -193,6 +208,7 @@ export const getEncoder = async ({ filter: '', }, + // h264 { encoder: 'h264_nvenc', enabled: false, @@ -230,10 +246,56 @@ export const getEncoder = async ({ outputArgs: [], filter: '', }, + + // av1 + { + encoder: 'av1_nvenc', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'av1_amf', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'av1_qsv', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'av1_vaapi', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, ]; const filteredGpuEncoders = gpuEncoders.filter((device) => encoderFilter(device.encoder, targetCodec)); + if (hardwareEncoding && hardwareType !== 'auto') { + const idx = filteredGpuEncoders.findIndex((device) => device.encoder.includes(hardwareType)); + + if (idx === -1) { + throw new Error(`Could not find encoder ${targetCodec} for hardware ${hardwareType}`); + } + + return { + ...filteredGpuEncoders[idx], + isGpu: true, + enabledDevices: [], + }; + } + + args.jobLog(JSON.stringify({ filteredGpuEncoders })); + // eslint-disable-next-line no-restricted-syntax for (const gpuEncoder of filteredGpuEncoders) { // eslint-disable-next-line no-await-in-loop @@ -242,10 +304,13 @@ export const getEncoder = async ({ encoder: gpuEncoder.encoder, inputArgs: gpuEncoder.inputArgs, filter: gpuEncoder.filter, + args, }); } - const enabledDevices = gpuEncoders.filter((device) => device.enabled === true); + const enabledDevices = filteredGpuEncoders.filter((device) => device.enabled === true); + + args.jobLog(JSON.stringify({ enabledDevices })); if (enabledDevices.length > 0) { if (enabledDevices[0].encoder.includes('nvenc')) { @@ -257,6 +322,7 @@ export const getEncoder = async ({ return { ...res, isGpu: true, + enabledDevices, }; } return { @@ -264,6 +330,7 @@ export const getEncoder = async ({ inputArgs: enabledDevices[0].inputArgs, outputArgs: enabledDevices[0].outputArgs, isGpu: true, + enabledDevices, }; } } @@ -274,6 +341,7 @@ export const getEncoder = async ({ inputArgs: [], outputArgs: [], isGpu: false, + enabledDevices: [], }; } if (targetCodec === 'h264') { return { @@ -281,6 +349,15 @@ export const getEncoder = async ({ inputArgs: [], outputArgs: [], isGpu: false, + enabledDevices: [], + }; + } if (targetCodec === 'av1') { + return { + encoder: 'libsvtav1', + inputArgs: [], + outputArgs: [], + isGpu: false, + enabledDevices: [], }; } @@ -289,5 +366,6 @@ export const getEncoder = async ({ inputArgs: [], outputArgs: [], isGpu: false, + enabledDevices: [], }; }; diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts b/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts index 29d4ebeaa..794be8b51 100644 --- a/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts +++ b/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts @@ -24,13 +24,22 @@ export interface IpluginInputs { export interface IpluginDetails { name: string, + nameUI?:{ + type: 'text' | 'textarea', + style?:Record, + } description: string, style: { borderColor: string, opacity?: number, + borderRadius?: number | string, + width?: number | string, + height?: number | string, + backgroundColor?: string, }, tags: string, isStartPlugin: boolean, + pType: 'start' | 'onFlowError' | '', sidebarPosition: number, icon: string, inputs: IpluginInputs[], @@ -39,6 +48,7 @@ export interface IpluginDetails { number: number, tooltip: string, }[], + requiresVersion: string, } export interface Ilog { @@ -67,7 +77,8 @@ export interface IffmpegCommand { } export interface Ivariables { - ffmpegCommand: IffmpegCommand + ffmpegCommand: IffmpegCommand, + flowFailed: boolean, } export interface IpluginOutputArgs { @@ -120,6 +131,22 @@ export interface IpluginInputArgs { // eslint-disable-next-line @typescript-eslint/no-explicit-any gracefulfs: any, // eslint-disable-next-line @typescript-eslint/no-explicit-any - mvdir: any + mvdir: any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + axios: any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + crudTransDBN: (collection: string, mode: string, docID: string, obj: any)=> any, }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + installClassicPluginDeps: (deps: string[]) => Promise, +} + +export interface IflowTemplate { + name: string, + description: string, + tags: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + flowPlugins:any[], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + flowEdges: any[], } diff --git a/tests/Community/Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js b/tests/Community/Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js index 78a7dbda7..e3f134cb3 100644 --- a/tests/Community/Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js +++ b/tests/Community/Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js @@ -80,7 +80,7 @@ const tests = [ linux: { processFile: true, preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv \n' - + ' -init_hw_device qsv:hw_any,child_device_type=vaapi -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -pix_fmt p010le ', + + ' -init_hw_device qsv:hw_any,child_device_type=vaapi -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -vf scale_qsv=format=p010le ', handBrakeMode: false, FFmpegMode: true, reQueueAfter: true, @@ -98,7 +98,7 @@ const tests = [ win32: { processFile: true, preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv \n' - + ' -init_hw_device qsv:hw_any,child_device_type=d3d11va -map 0 -c:v hevc_qsv -load_plugin hevc_hw -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -pix_fmt p010le ', + + ' -init_hw_device qsv:hw_any,child_device_type=d3d11va -map 0 -c:v hevc_qsv -load_plugin hevc_hw -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -vf scale_qsv=format=p010le ', handBrakeMode: false, FFmpegMode: true, reQueueAfter: true, @@ -115,7 +115,7 @@ const tests = [ }, darwin: { processFile: true, - preset: '-fflags +genpts -hwaccel videotoolbox -map 0 -c:v hevc_videotoolbox -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -pix_fmt p010le ', + preset: '-fflags +genpts -hwaccel videotoolbox -map 0 -c:v hevc_videotoolbox -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -vf scale_qsv=format=p010le ', handBrakeMode: false, FFmpegMode: true, reQueueAfter: true,