From 25c4fab8d9569b9905b2c0a16db412addce5d1b1 Mon Sep 17 00:00:00 2001 From: HaveAGitGat <43864057+HaveAGitGat@users.noreply.github.com> Date: Sat, 26 Aug 2023 17:53:07 +0100 Subject: [PATCH] Update flows --- .../runClassicFilterPlugin/1.0.0/index.js | 139 ++++++++ .../runClassicTranscodePlugin/1.0.0/index.js | 262 ++++++++++++++ .../ffmpegCommand10BitVideo/1.0.0/index.js | 7 +- .../ffmpegCommandExecute/1.0.0/index.js | 118 ++++--- .../1.0.0/index.js | 1 - .../1.0.0/index.js | 1 - .../ffmpegCommandRorderStreams/1.0.0/index.js | 143 +++++++- .../ffmpegCommandSetContainer/1.0.0/index.js | 3 +- .../1.0.0/index.js | 83 +++++ .../1.0.0/index.js | 24 +- .../1.0.0/index.js | 168 ++++++++- .../ffmpegCommandStart/1.0.0/index.js | 9 +- .../file/checkFileExtension/1.0.0/index.js | 57 +++ .../file/checkFileSize/1.0.0/index.js | 92 +++++ .../file/compareFileSize/1.0.0/index.js | 53 +++ .../file/copyToDirectory/1.0.0/index.js | 100 +++++- .../file/moveToDirectory/1.0.0/index.js | 1 + .../file/moveToDirectory/2.0.0/index.js | 85 ++++- .../file/replaceOriginalFile/1.0.0/index.js | 20 +- .../file/setOriginalFile/1.0.0/index.js | 37 ++ .../unpack}/1.0.0/index.js | 10 +- .../handbrakeCustomArguments/1.0.0/index.js | 165 +++++++++ .../input/inputFile/1.0.0/index.js | 2 +- .../tools/failFlow/1.0.0/index.js | 31 ++ .../tools/goToFlow/1.0.0/index.js | 31 ++ .../{unpack => runMkvPropEdit}/1.0.0/index.js | 77 ++--- .../tools/webRequest/1.0.0/index.js | 109 +----- .../video/check10Bit/1.0.0/index.js | 46 +++ .../video/checkVideoBitrate/1.0.0/index.js | 93 +++++ .../video/checkVideoCodec/1.0.0/index.js | 13 +- .../video/basicVideo.js | 178 +++------- .../1.0.0/{utils.js => cliUtils.js} | 11 +- FlowPlugins/FlowHelpers/1.0.0/fileUtils.js | 17 + .../FlowHelpers/1.0.0/hardwareUtils.js | 325 ++++++++++++++++++ .../FlowHelpers/1.0.0/hardwareUtils.test.js | 67 ++++ .../runClassicFilterPlugin/1.0.0/index.ts | 115 +++++++ .../runClassicTranscodePlugin/1.0.0/index.ts | 237 +++++++++++++ .../ffmpegCommand10BitVideo/1.0.0/index.ts | 8 +- .../ffmpegCommandExecute/1.0.0/index.ts | 118 +++++-- .../1.0.0/index.ts | 1 - .../1.0.0/index.ts | 1 - .../ffmpegCommandRorderStreams/1.0.0/index.ts | 197 ++++++++++- .../ffmpegCommandSetContainer/1.0.0/index.ts | 3 +- .../1.0.0/index.ts | 103 ++++++ .../1.0.0/index.ts | 25 +- .../1.0.0/index.ts | 114 +++++- .../ffmpegCommandStart/1.0.0/index.ts | 12 +- .../file/checkFileExtension/1.0.0/index.ts | 68 ++++ .../file/checkFileSize/1.0.0/index.ts | 100 ++++++ .../file/compareFileSize/1.0.0/index.ts | 62 ++++ .../file/copyToDirectory/1.0.0/index.ts | 47 ++- .../file/moveToDirectory/1.0.0/index.ts | 1 + .../file/moveToDirectory/2.0.0/index.ts | 28 +- .../file/replaceOriginalFile/1.0.0/index.ts | 20 +- .../file/setOriginalFile/1.0.0/index.ts | 44 +++ .../unpack}/1.0.0/index.ts | 12 +- .../handbrakeCustomArguments/1.0.0/index.ts | 135 ++++++++ .../input/inputFile/1.0.0/index.ts | 2 +- .../tools/failFlow/1.0.0/index.ts | 39 +++ .../tools/goToFlow/1.0.0/index.ts | 38 ++ .../tools/runMkvPropEdit/1.0.0/index.ts | 67 ++++ .../tools/unpack/1.0.0/index.ts | 94 ----- .../tools/webRequest/1.0.0/index.ts | 57 +-- .../video/check10Bit/1.0.0/index.ts | 55 +++ .../video/checkVideoBitrate/1.0.0/index.ts | 102 ++++++ .../video/checkVideoCodec/1.0.0/index.ts | 13 +- .../video/basicVideo.ts | 178 +++------- .../1.0.0/{utils.ts => cliUtils.ts} | 12 +- FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts | 13 + .../FlowHelpers/1.0.0/hardwareUtils.test.ts | 24 ++ .../FlowHelpers/1.0.0/hardwareUtils.ts | 293 ++++++++++++++++ .../1.0.0/interfaces/interfaces.ts | 37 +- .../1.0.0/interfaces/synced/IFileObject.ts | 3 +- 73 files changed, 4256 insertions(+), 800 deletions(-) create mode 100644 FlowPlugins/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.js create mode 100644 FlowPlugins/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.js create mode 100644 FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.js create mode 100644 FlowPlugins/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.js create mode 100644 FlowPlugins/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.js create mode 100644 FlowPlugins/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.js create mode 100644 FlowPlugins/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.js rename FlowPlugins/CommunityFlowPlugins/{ffmpegCommand/ffmpegCommandSetVideoScale => file/unpack}/1.0.0/index.js (87%) create mode 100644 FlowPlugins/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.js create mode 100644 FlowPlugins/CommunityFlowPlugins/tools/failFlow/1.0.0/index.js create mode 100644 FlowPlugins/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.js rename FlowPlugins/CommunityFlowPlugins/tools/{unpack => runMkvPropEdit}/1.0.0/index.js (65%) create mode 100644 FlowPlugins/CommunityFlowPlugins/video/check10Bit/1.0.0/index.js create mode 100644 FlowPlugins/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.js rename FlowPlugins/FlowHelpers/1.0.0/{utils.js => cliUtils.js} (96%) create mode 100644 FlowPlugins/FlowHelpers/1.0.0/fileUtils.js create mode 100644 FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.js create mode 100644 FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.test.js create mode 100644 FlowPluginsTs/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.ts rename FlowPluginsTs/CommunityFlowPlugins/{ffmpegCommand/ffmpegCommandSetVideoScale => file/unpack}/1.0.0/index.ts (84%) create mode 100644 FlowPluginsTs/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/tools/failFlow/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.ts delete mode 100644 FlowPluginsTs/CommunityFlowPlugins/tools/unpack/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/video/check10Bit/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.ts rename FlowPluginsTs/FlowHelpers/1.0.0/{utils.ts => cliUtils.ts} (95%) create mode 100644 FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts create mode 100644 FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.test.ts create mode 100644 FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.ts diff --git a/FlowPlugins/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.js new file mode 100644 index 0000000..2b7b255 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.js @@ -0,0 +1,139 @@ +"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: 'Run Classic Filter Plugin', + description: 'Run one of Tdarr\'s classic plugins that has Operation: Filter', + style: { + borderColor: 'orange', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'pluginSourceId', + type: 'string', + defaultValue: 'Community:Tdarr_Plugin_00td_filter_by_codec', + inputUI: { + type: 'dropdown', + options: [], + }, + tooltip: 'Specify the classic plugin ID', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File met conditions, would traditionally continue to next plugin in plugin stack', + }, + { + number: 2, + tooltip: 'File did not meet conditions, would traditionally break out of plugin stack', + }, + ], +}); }; +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; + 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(); + args.jobLog(JSON.stringify(result, null, 2)); + outputNumber = result.processFile ? 1 : 2; + return [2 /*return*/, { + outputFileObj: args.inputFileObj, + outputNumber: outputNumber, + variables: args.variables, + }]; + } + }); +}); }; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.js new file mode 100644 index 0000000..f82d88b --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.js @@ -0,0 +1,262 @@ +"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 }; + } +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +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: 'Run Classic Transcode Plugin', + description: 'Run one of Tdarr\'s classic plugins that has Operation: Transcode', + style: { + borderColor: 'green', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'pluginSourceId', + type: 'string', + defaultValue: 'Community:Tdarr_Plugin_MC93_Migz1FFMPEG', + inputUI: { + type: 'dropdown', + options: [], + }, + tooltip: 'Specify the classic plugin ID', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +var replaceContainer = function (filePath, container) { + var parts = filePath.split('.'); + parts[parts.length - 1] = container.split('.').join(''); + return parts.join('.'); +}; +// 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 _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(); + args.jobLog(JSON.stringify(result, null, 2)); + // --- Backwards compatibility------------ + if (result.handBrakeMode) { + result.handbrakeMode = result.handBrakeMode; + } + if (result.FFmpegMode) { + result.ffmpegMode = result.FFmpegMode; + } + //---------------------------------------- + if (result.ffmpegMode) { + result.cliToUse = 'ffmpeg'; + } + else if (result.handbrakeMode) { + result.cliToUse = 'handbrake'; + } + else if (typeof ((_a = result === null || result === void 0 ? void 0 : result.custom) === null || _a === void 0 ? void 0 : _a.cliPath) === 'string') { + cliPath_1 = result.custom.cliPath; + if (cliPath_1.toLowerCase().includes('ffmpeg')) { + result.cliToUse = 'ffmpeg'; + } + else if (cliPath_1.toLowerCase().includes('handbrake')) { + result.cliToUse = 'handbrake'; + } + else if (cliPath_1.toLowerCase().includes('editready')) { + result.cliToUse = 'editready'; + } + else if (cliPath_1.toLowerCase().includes('av1an')) { + result.cliToUse = 'av1an'; + } + } + result.workerLog = result.transcodeSettingsLog; + args.jobLog(JSON.stringify(result, null, 2)); + if (result.error) { + throw new Error("Plugin ".concat(absolutePath, " failed: ").concat(result.error)); + } + if (result.processFile !== true) { + return [2 /*return*/, { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }]; + } + 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); + if (!isCustomConfig) { + cacheFilePath = replaceContainer(cacheFilePath, result.container); + } + else { + cacheFilePath = result.custom.outputPath; + } + if (result.preset.includes('')) { + presetSplit = result.preset.split(''); + } + else { + presetSplit = result.preset.split(','); + } + workerCommand = []; + cliPath = ''; + if (isCustomConfig) { + 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; + } + else { + workerCommand = __spreadArray([], args.deps.parseArgsStringToArgv(customArgs, '', ''), true); + } + } + else { + // working on windows with '` and spaces + // working on unix with ' + switch (true) { + case result.cliToUse === 'handbrake': + workerCommand = __spreadArray([ + '-i', + "".concat(args.inputFileObj._id), + '-o', + "".concat(cacheFilePath) + ], args.deps.parseArgsStringToArgv(result.preset, '', ''), true); + cliPath = "".concat(args.handbrakePath); + break; + case result.cliToUse === 'ffmpeg': + workerCommand = __spreadArray(__spreadArray(__spreadArray(__spreadArray([], args.deps.parseArgsStringToArgv(presetSplit[0], '', ''), true), [ + '-i', + "".concat(args.inputFileObj._id) + ], false), args.deps.parseArgsStringToArgv(presetSplit[1], '', ''), true), [ + "".concat(cacheFilePath), + ], false); + cliPath = "".concat(args.ffmpegPath); + break; + default: + } + } + cli = new cliUtils_1.CLI({ + cli: cliPath, + spawnArgs: workerCommand, + spawnOpts: {}, + jobLog: args.jobLog, + outputFilePath: cacheFilePath, + inputFileObj: args.inputFileObj, + logFullCliOutput: args.logFullCliOutput, + updateWorker: args.updateWorker, + }); + return [4 /*yield*/, cli.runCli()]; + case 5: + res = _d.sent(); + if (res.cliExitCode !== 0) { + args.jobLog("Running ".concat(cliPath, " failed")); + throw new Error("Running ".concat(cliPath, " failed")); + } + args.logOutcome('tSuc'); + return [2 /*return*/, { + outputFileObj: { + _id: cacheFilePath, + }, + outputNumber: 1, + variables: args.variables, + }]; + } + }); +}); }; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.js index 0990ba6..2650072 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.js @@ -7,7 +7,6 @@ var details = function () { return ({ description: 'Set 10 Bit Video', style: { borderColor: '#6efefc', - opacity: 0.5, }, tags: 'video', isStartPlugin: false, @@ -27,6 +26,12 @@ 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); + 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.outputArgs.push('-pix_fmt:v:{outputTypeIndex}', 'p010le', '-profile:v:{outputTypeIndex}', 'main10'); + } + } return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.js index 70bf342..28322e8 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.js @@ -35,9 +35,19 @@ var __generator = (this && this.__generator) || function (thisArg, body) { if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.plugin = exports.details = void 0; -var utils_1 = require("../../../../FlowHelpers/1.0.0/utils"); +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', @@ -53,30 +63,40 @@ var details = function () { return ({ outputs: [ { number: 1, - tooltip: 'File is 480p', - }, - { - number: 2, - tooltip: 'File is 576p', + tooltip: 'Continue to next plugin', }, ], }); }; exports.details = details; -var getEncoder = function (codec) { - switch (codec) { - case 'h264': - return 'libx264'; - case 'hevc': - return 'libx265'; - default: - return codec; +var getOuputStreamIndex = function (streams, stream) { + var index = -1; + for (var idx = 0; idx < streams.length; idx += 1) { + if (!stream.removed) { + index += 1; + } + if (streams[idx].index === stream.index) { + break; + } } + return index; +}; +var getOuputStreamTypeIndex = function (streams, stream) { + var index = -1; + for (var idx = 0; idx < streams.length; idx += 1) { + if (!stream.removed && streams[idx].codec_type === stream.codec_type) { + index += 1; + } + if (streams[idx].index === stream.index) { + break; + } + } + return index; }; // eslint-disable-next-line @typescript-eslint/no-unused-vars var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () { - var lib, cliArgs, shouldProcess, outputFilePath, cli, res; - return __generator(this, function (_a) { - switch (_a.label) { + var lib, cliArgs, inputArgs, _a, shouldProcess, streams, _loop_1, i, idx, outputFilePath, cli, res; + 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 @@ -85,23 +105,42 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function cliArgs.push('-y'); cliArgs.push('-i'); cliArgs.push(args.inputFileObj._id); - shouldProcess = false; - // @ts-expect-error type - args.variables.ffmpegCommand.streams.forEach(function (stream) { - if (!stream.removed) { - cliArgs.push('-map'); - cliArgs.push("0:".concat(stream.index)); - cliArgs.push("-c:".concat(stream.index)); - args.jobLog(JSON.stringify({ stream: stream })); - if (args.inputs.forceProcess || stream.codec_name !== stream.targetCodec) { - shouldProcess = true; - cliArgs.push(getEncoder(stream.targetCodec)); + inputArgs = []; + _a = args.variables.ffmpegCommand, shouldProcess = _a.shouldProcess, streams = _a.streams; + streams = streams.filter(function (stream) { + if (stream.removed) { + shouldProcess = true; + } + return !stream.removed; + }); + if ((0, fileUtils_1.getContainer)(args.inputFileObj._id) !== args.variables.ffmpegCommand.container) { + shouldProcess = true; + } + _loop_1 = function (i) { + var stream = streams[i]; + stream.outputArgs = stream.outputArgs.map(function (arg) { + if (arg.includes('{outputIndex}')) { + // eslint-disable-next-line no-param-reassign + arg = arg.replace('{outputIndex}', String(getOuputStreamIndex(streams, stream))); } - else { - cliArgs.push('copy'); + if (arg.includes('{outputTypeIndex}')) { + // eslint-disable-next-line no-param-reassign + arg = arg.replace('{outputTypeIndex}', String(getOuputStreamTypeIndex(streams, stream))); } + return arg; + }); + cliArgs.push.apply(cliArgs, stream.mapArgs); + if (stream.outputArgs.length === 0) { + cliArgs.push("-c:".concat(getOuputStreamIndex(streams, stream)), 'copy'); } - }); + else { + cliArgs.push.apply(cliArgs, stream.outputArgs); + } + inputArgs.push.apply(inputArgs, stream.inputArgs); + }; + for (i = 0; i < streams.length; i += 1) { + _loop_1(i); + } if (!shouldProcess) { args.jobLog('No need to process file, already as required'); return [2 /*return*/, { @@ -110,16 +149,20 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function variables: args.variables, }]; } - outputFilePath = "".concat(args.workDir, "/tempFile.").concat(args.variables.ffmpegCommand.container); + 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(outputFilePath); - // @ts-expect-error type - args.deps.fsextra.ensureDirSync(args.workDir); args.jobLog('Processing file'); args.jobLog(JSON.stringify({ cliArgs: cliArgs, outputFilePath: outputFilePath, })); - cli = new utils_1.CLI({ + args.updateWorker({ + CLIType: args.ffmpegPath, + preset: cliArgs.join(' '), + }); + cli = new cliUtils_1.CLI({ cli: args.ffmpegPath, spawnArgs: cliArgs, spawnOpts: {}, @@ -131,10 +174,7 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function }); return [4 /*yield*/, cli.runCli()]; case 1: - res = _a.sent(); - if (!args.logFullCliOutput) { - args.jobLog(res.errorLogFull.slice(-1000).join('')); - } + res = _b.sent(); if (res.cliExitCode !== 0) { args.jobLog('Running FFmpeg failed'); throw new Error('FFmpeg failed'); diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.js index 85715e9..97b0d1c 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.js @@ -27,7 +27,6 @@ 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); - // @ts-expect-error type args.variables.ffmpegCommand.streams.forEach(function (stream) { if (stream.codec_type === 'data') { stream.removed = true; diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.js index 2296e7a..7413f8e 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.js @@ -27,7 +27,6 @@ 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); - // @ts-expect-error type args.variables.ffmpegCommand.streams.forEach(function (stream) { if (stream.codec_type === 'subtitle') { stream.removed = true; diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.js index 1da68b5..54e675c 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.js @@ -3,17 +3,62 @@ 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 Bitrate', - description: 'Set Video Bitrate', + name: 'Reorder Streams', + description: 'Reorder Streams', style: { borderColor: '#6efefc', - opacity: 0.5, }, tags: 'video', isStartPlugin: false, sidebarPosition: -1, icon: '', - inputs: [], + inputs: [ + { + name: 'processOrder', + type: 'string', + defaultValue: 'codecs,channels,languages,streamTypes', + inputUI: { + type: 'text', + }, + tooltip: "Specify the process order.\nFor example, if 'languages' is first, the streams will be ordered based on that first.\nSo put the most important properties last.\nThe default order is suitable for most people.\n\n \\nExample:\\n\n codecs,channels,languages,streamTypes\n ", + }, + { + name: 'languages', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: "Specify the language tags order, separated by commas. Leave blank to disable.\n \\nExample:\\n\n eng,fre\n ", + }, + { + name: 'channels', + type: 'string', + defaultValue: '7.1,5.1,2,1', + inputUI: { + type: 'text', + }, + tooltip: "Specify the channels order, separated by commas. Leave blank to disable.\n \n \\nExample:\\n\n 7.1,5.1,2,1", + }, + { + name: 'codecs', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: "Specify the codec order, separated by commas. Leave blank to disable.\n \n \\nExample:\\n\n aac,ac3", + }, + { + name: 'streamTypes', + type: 'string', + defaultValue: 'video,audio,subtitle', + inputUI: { + type: 'text', + }, + tooltip: "Specify the streamTypes order, separated by commas. Leave blank to disable.\n \\nExample:\\n\n video,audio,subtitle\n ", + }, + ], outputs: [ { number: 1, @@ -27,6 +72,96 @@ 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 streams = JSON.parse(JSON.stringify(args.variables.ffmpegCommand.streams)); + var originalStreams = JSON.stringify(streams); + streams.forEach(function (stream, index) { + // eslint-disable-next-line no-param-reassign + stream.typeIndex = index; + }); + var sortStreams = function (sortType) { + var items = sortType.inputs.split(','); + items.reverse(); + for (var i = 0; i < items.length; i += 1) { + var matchedStreams = []; + for (var j = 0; j < streams.length; j += 1) { + if (String(sortType.getValue(streams[j])) === String(items[i])) { + if (streams[j].codec_long_name + && (streams[j].codec_long_name.includes('image') + || streams[j].codec_name.includes('png'))) { + // do nothing, ffmpeg bug, doesn't move image streams + } + else { + matchedStreams.push(streams[j]); + streams.splice(j, 1); + j -= 1; + } + } + } + streams = matchedStreams.concat(streams); + } + }; + var processOrder = String(args.inputs.processOrder); + var _a = args.inputs, languages = _a.languages, codecs = _a.codecs, channels = _a.channels, streamTypes = _a.streamTypes; + var sortTypes = { + languages: { + getValue: function (stream) { + var _a; + if ((_a = stream === null || stream === void 0 ? void 0 : stream.tags) === null || _a === void 0 ? void 0 : _a.language) { + return stream.tags.language; + } + return ''; + }, + inputs: languages, + }, + codecs: { + getValue: function (stream) { + try { + return stream.codec_name; + } + catch (err) { + // err + } + return ''; + }, + inputs: codecs, + }, + channels: { + getValue: function (stream) { + var chanMap = { + 8: '7.1', + 6: '5.1', + 2: '2', + 1: '1', + }; + if ((stream === null || stream === void 0 ? void 0 : stream.channels) && chanMap[stream.channels]) { + return chanMap[stream.channels]; + } + return ''; + }, + inputs: channels, + }, + streamTypes: { + getValue: function (stream) { + if (stream.codec_type) { + return stream.codec_type; + } + return ''; + }, + inputs: streamTypes, + }, + }; + var processOrderArr = processOrder.split(','); + for (var k = 0; k < processOrderArr.length; k += 1) { + if (sortTypes[processOrderArr[k]] && sortTypes[processOrderArr[k]].inputs) { + sortStreams(sortTypes[processOrderArr[k]]); + } + } + if (JSON.stringify(streams) !== originalStreams) { + // eslint-disable-next-line no-param-reassign + args.variables.ffmpegCommand.shouldProcess = true; + // eslint-disable-next-line no-param-reassign + args.variables.ffmpegCommand.streams = streams; + } return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.js index 9c93015..a655e4b 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.js @@ -41,8 +41,7 @@ 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); - // @ts-expect-error type - args.variables.ffmpegCommand.container = args.inputs.container; + args.variables.ffmpegCommand.container = String(args.inputs.container); return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.js new file mode 100644 index 0000000..9295b98 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.js @@ -0,0 +1,83 @@ +"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 Resolution', + description: 'Change video resolution', + style: { + borderColor: '#6efefc', + }, + tags: 'video', + isStartPlugin: false, + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'targetResolution', + type: 'string', + defaultValue: '1080p', + inputUI: { + type: 'dropdown', + options: [ + '480p', + '720p', + '1080p', + '1440p', + '4KUHD', + ], + }, + tooltip: 'Specify the codec to use', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); }; +exports.details = details; +var getVfScale = function (targetResolution) { + switch (targetResolution) { + case '480p': + return ['-vf', 'scale=720:-2']; + case '576p': + return ['-vf', 'scale=720:-2']; + case '720p': + return ['-vf', 'scale=1280:-2']; + case '1080p': + return ['-vf', 'scale=1920:-2']; + case '1440p': + return ['-vf', 'scale=2560:-2']; + case '4KUHD': + return ['-vf', 'scale=3840:-2']; + default: + return ['-vf', 'scale=1920:-2']; + } +}; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +var plugin = function (args) { + var _a; + var lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + for (var i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { + var stream = args.variables.ffmpegCommand.streams[i]; + if (stream.codec_type === 'video') { + var targetResolution = String(args.inputs.targetResolution); + if (targetResolution !== args.inputFileObj.video_resolution) { + // eslint-disable-next-line no-param-reassign + args.variables.ffmpegCommand.shouldProcess = true; + var scaleArgs = getVfScale(targetResolution); + (_a = stream.outputArgs).push.apply(_a, scaleArgs); + } + } + } + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.js index 1dc36c4..61e49e3 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.js @@ -1,19 +1,29 @@ "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: 'Reorder Streams', - description: 'Reorder Streams', + name: 'Set Video Bitrate', + description: 'Set Video Bitrate', style: { borderColor: '#6efefc', - opacity: 0.5, }, tags: 'video', isStartPlugin: false, sidebarPosition: -1, icon: '', - inputs: [], + inputs: [ + { + name: 'bitrate', + type: 'string', + defaultValue: '5000', + inputUI: { + type: 'text', + }, + tooltip: 'Specify bitrate in kbps', + }, + ], outputs: [ { number: 1, @@ -27,6 +37,12 @@ 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); + args.variables.ffmpegCommand.streams.forEach(function (stream) { + if (stream.codec_type === 'video') { + var ffType = (0, fileUtils_1.getFfType)(stream.codec_type); + stream.outputArgs.push("-b:".concat(ffType, ":{outputTypeIndex}"), "".concat(String(args.inputs.bitrate), "k")); + } + }); return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.js index 23ce59c..411a2ca 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.js @@ -1,7 +1,44 @@ "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 hardwareUtils_1 = require("../../../../FlowHelpers/1.0.0/hardwareUtils"); /* eslint-disable no-param-reassign */ var details = function () { return ({ name: 'Set Video Encoder', @@ -15,7 +52,7 @@ var details = function () { return ({ icon: '', inputs: [ { - name: 'targetCodec', + name: 'outputCodec', type: 'string', defaultValue: 'hevc', inputUI: { @@ -29,6 +66,61 @@ var details = function () { return ({ }, tooltip: 'Specify the codec to use', }, + { + name: 'ffmpegPreset', + type: 'string', + defaultValue: 'fast', + inputUI: { + type: 'dropdown', + options: [ + 'veryslow', + 'slower', + 'slow', + 'medium', + 'fast', + 'faster', + 'veryfast', + 'superfast', + 'ultrafast', + ], + }, + tooltip: 'Specify the codec to use', + }, + { + name: 'ffmpegQuality', + type: 'number', + defaultValue: '25', + inputUI: { + type: 'text', + }, + tooltip: 'Specify the codec to use', + }, + { + name: 'hardwareEncoding', + type: 'boolean', + defaultValue: 'true', + inputUI: { + type: 'dropdown', + options: [ + 'false', + 'true', + ], + }, + tooltip: 'Specify whether to use hardware encoding if available', + }, + { + name: 'hardwareDecoding', + type: 'boolean', + defaultValue: 'true', + inputUI: { + type: 'dropdown', + options: [ + 'false', + 'true', + ], + }, + tooltip: 'Specify whether to use hardware decoding if available', + }, { name: 'forceEncoding', type: 'boolean', @@ -52,22 +144,64 @@ 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); - // @ts-expect-error type - args.variables.ffmpegCommand.streams.forEach(function (stream) { - if (stream.codec_type === 'video') { - // @ts-expect-error type - stream.targetCodec = args.inputs.targetCodec; - stream.forceEncoding = args.inputs.forceEncoding; +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 _a, _b; + return __generator(this, function (_c) { + switch (_c.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); + hardwareDecoding = args.inputs.hardwareDecoding === true; + args.variables.ffmpegCommand.hardwareDecoding = hardwareDecoding; + i = 0; + _c.label = 1; + case 1: + if (!(i < args.variables.ffmpegCommand.streams.length)) return [3 /*break*/, 4]; + stream = args.variables.ffmpegCommand.streams[i]; + if (!(stream.codec_type === 'video')) return [3 /*break*/, 3]; + targetCodec = String(args.inputs.outputCodec); + ffmpegPreset = String(args.inputs.ffmpegPreset); + ffmpegQuality = String(args.inputs.ffmpegQuality); + forceEncoding = args.inputs.forceEncoding === true; + hardwarEncoding = args.inputs.hardwareEncoding === true; + if (!(forceEncoding + || stream.codec_name !== targetCodec)) return [3 /*break*/, 3]; + args.variables.ffmpegCommand.shouldProcess = true; + return [4 /*yield*/, (0, hardwareUtils_1.getEncoder)({ + targetCodec: targetCodec, + hardwareEncoding: hardwarEncoding, + args: args, + })]; + case 2: + encoderProperties = _c.sent(); + stream.outputArgs.push('-c:{outputIndex}', encoderProperties.encoder); + if (encoderProperties.isGpu) { + stream.outputArgs.push('-qp', ffmpegQuality); + } + else { + stream.outputArgs.push('-crf', ffmpegQuality); + } + if (ffmpegPreset) { + stream.outputArgs.push('-preset', ffmpegPreset); + } + if (hardwareDecoding) { + (_a = stream.inputArgs).push.apply(_a, encoderProperties.inputArgs); + } + if (encoderProperties.outputArgs) { + (_b = stream.outputArgs).push.apply(_b, encoderProperties.outputArgs); + } + _c.label = 3; + case 3: + i += 1; + return [3 /*break*/, 1]; + case 4: return [2 /*return*/, { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }]; } }); - return { - outputFileObj: args.inputFileObj, - outputNumber: 1, - variables: args.variables, - }; -}; +}); }; exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.js index c677c4c..206acdf 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.js @@ -42,8 +42,15 @@ var plugin = function (args) { var container = containerParts[containerParts.length - 1]; var ffmpegCommand = { inputFiles: [], - streams: JSON.parse(JSON.stringify(args.inputFileObj.ffProbeData.streams)).map(function (stream) { return (__assign(__assign({}, stream), { removed: false, targetCodec: stream.codec_name, args: [] })); }), + streams: JSON.parse(JSON.stringify(args.inputFileObj.ffProbeData.streams)).map(function (stream) { return (__assign(__assign({}, stream), { removed: false, mapArgs: [ + '-map', + "0:".concat(stream.index), + ], inputArgs: [], outputArgs: [] })); }), container: container, + hardwareDecoding: false, + shouldProcess: false, + overallInputArguments: [], + overallOuputArguments: [], }; args.variables.ffmpegCommand = ffmpegCommand; return { diff --git a/FlowPlugins/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.js new file mode 100644 index 0000000..72f6c60 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.js @@ -0,0 +1,57 @@ +"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 Extension', + description: 'Check file extension', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'extensions', + type: 'string', + defaultValue: 'mkv,mp4', + inputUI: { + type: 'text', + }, + tooltip: 'A comma separated list of extensions to check', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File is one of extensions', + }, + { + number: 2, + tooltip: 'File is not one of extensions', + }, + ], +}); }; +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 extensions = String(args.inputs.extensions); + var extensionArray = extensions.trim().split(','); + var extension = (0, fileUtils_1.getContainer)(args.inputFileObj._id); + var extensionMatch = false; + if (extensionArray.includes(extension)) { + extensionMatch = true; + } + return { + outputFileObj: args.inputFileObj, + outputNumber: extensionMatch ? 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 new file mode 100644 index 0000000..620c13b --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.js @@ -0,0 +1,92 @@ +"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 File Size', + description: 'Check size of working file', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'unit', + type: 'string', + defaultValue: 'GB', + inputUI: { + type: 'dropdown', + options: [ + 'B', + 'KB', + 'MB', + 'GB', + ], + }, + 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 greaterThanBytes = Number(args.inputs.greaterThan); + var lessThanBytes = Number(args.inputs.lessThan); + var fileSizeBytes = args.inputFileObj.file_size * 1000 * 1000; + if (args.inputs.unit === 'KB') { + greaterThanBytes *= 1000; + lessThanBytes *= 1000; + } + else if (args.inputs.unit === 'MB') { + greaterThanBytes *= 1000000; + lessThanBytes *= 1000000; + } + else if (args.inputs.unit === 'GB') { + greaterThanBytes *= 1000000000; + lessThanBytes *= 1000000000; + } + if (fileSizeBytes >= greaterThanBytes && fileSizeBytes <= lessThanBytes) { + isWithinRange = true; + } + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.js new file mode 100644 index 0000000..dfbf12e --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.js @@ -0,0 +1,53 @@ +"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', + description: 'Compare file size of working file compared to original file', + style: { + borderColor: 'orange', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Working file is smaller than original file', + }, + { + number: 2, + tooltip: 'Working file is same size as original file', + }, + { + number: 3, + tooltip: 'Working file is larger than original file', + }, + ], +}); }; +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 outputNumber = 1; + if (args.inputFileObj.file_size < args.originalLibraryFile.file_size) { + outputNumber = 1; + } + else if (args.inputFileObj.file_size === args.originalLibraryFile.file_size) { + outputNumber = 2; + } + else if (args.inputFileObj.file_size > args.originalLibraryFile.file_size) { + outputNumber = 3; + } + 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 3a5b507..5440e3e 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.js @@ -1,13 +1,50 @@ "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 fs_1 = require("fs"); +var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ name: 'Copy to Directory', description: 'Copy the working file to a directory', style: { borderColor: 'green', - opacity: 0.5, }, tags: '', isStartPlugin: false, @@ -15,19 +52,26 @@ var details = function () { return ({ icon: 'faArrowRight', inputs: [ { - name: 'target_codec', + name: 'outputDirectory', type: 'string', - defaultValue: 'hevc', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Specify ouput directory', + }, + { + name: 'makeWorkingFile', + type: 'boolean', + defaultValue: 'false', inputUI: { - type: 'dropdown', + type: 'text', options: [ - 'hevc', - // 'vp9', - 'h264', - // 'vp8', + 'false', + 'true', ], }, - tooltip: 'Specify the codec to use', + tooltip: 'Make the copied file the working file', }, ], outputs: [ @@ -39,14 +83,32 @@ 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, originalFileName, newContainer, outputPath, workingFile; + 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); + originalFileName = (0, fileUtils_1.getFileName)(args.originalLibraryFile._id); + newContainer = (0, fileUtils_1.getContainer)(args.inputFileObj._id); + outputPath = "".concat(args.inputs.outputDirectory, "/").concat(originalFileName, ".").concat(newContainer); + return [4 /*yield*/, fs_1.promises.copyFile(args.inputFileObj._id, outputPath)]; + case 1: + _a.sent(); + workingFile = args.inputFileObj._id; + if (args.inputs.makeWorkingFile) { + workingFile = outputPath; + } + return [2 /*return*/, { + outputFileObj: { + _id: workingFile, + }, + outputNumber: 1, + variables: args.variables, + }]; + } + }); +}); }; exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.js index 0f6173d..7525c8f 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.js @@ -7,6 +7,7 @@ var details = function () { return ({ description: 'Move working file to directory.', style: { borderColor: 'green', + opacity: 0.5, }, tags: '', isStartPlugin: false, diff --git a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js index b92449a..6cab7e2 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js @@ -1,19 +1,66 @@ "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 fs_1 = require("fs"); +var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ name: 'Move To Directory', description: 'Move working file to directory.', style: { borderColor: 'green', - opacity: 0.5, }, tags: '', isStartPlugin: false, sidebarPosition: -1, icon: 'faArrowRight', - inputs: [], + inputs: [ + { + name: 'outputDirectory', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Specify ouput directory', + }, + ], outputs: [ { number: 1, @@ -23,14 +70,28 @@ 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, originalFileName, newContainer, outputPath; + 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); + originalFileName = (0, fileUtils_1.getFileName)(args.originalLibraryFile._id); + newContainer = (0, fileUtils_1.getContainer)(args.inputFileObj._id); + outputPath = "".concat(args.inputs.outputDirectory, "/").concat(originalFileName, ".").concat(newContainer); + return [4 /*yield*/, fs_1.promises.rename(args.inputFileObj._id, outputPath)]; + case 1: + _a.sent(); + return [2 /*return*/, { + outputFileObj: { + _id: outputPath, + }, + 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 5565bd6..5adc035 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js @@ -40,7 +40,7 @@ exports.plugin = exports.details = void 0; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ name: 'Replace Original File', - description: 'Replace the origial file', + description: 'Replace the original file', style: { borderColor: 'green', }, @@ -48,23 +48,7 @@ var details = function () { return ({ isStartPlugin: false, sidebarPosition: -1, icon: 'faArrowRight', - inputs: [ - { - name: 'target_codec', - type: 'string', - defaultValue: 'hevc', - inputUI: { - type: 'dropdown', - options: [ - 'hevc', - // 'vp9', - 'h264', - // 'vp8', - ], - }, - tooltip: 'Specify the codec to use', - }, - ], + inputs: [], outputs: [ { number: 1, diff --git a/FlowPlugins/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.js new file mode 100644 index 0000000..1ca5b52 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/file/setOriginalFile/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: 'Set Original File', + description: 'Set the working file to the original file', + style: { + borderColor: 'green', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: '', + 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: { + _id: args.originalLibraryFile._id, + }, + outputNumber: 1, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoScale/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/unpack/1.0.0/index.js similarity index 87% rename from FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoScale/1.0.0/index.js rename to FlowPlugins/CommunityFlowPlugins/file/unpack/1.0.0/index.js index 4838ddf..a6210f3 100644 --- a/FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoScale/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/unpack/1.0.0/index.js @@ -3,16 +3,16 @@ 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 Scale', - description: 'Change video scale', + name: 'Unpack File', + description: 'Unpack a file', style: { - borderColor: '#6efefc', + borderColor: 'green', opacity: 0.5, }, - tags: 'video', + tags: '', isStartPlugin: false, sidebarPosition: -1, - icon: '', + icon: 'faArrowRight', inputs: [], outputs: [ { diff --git a/FlowPlugins/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.js new file mode 100644 index 0000000..e10d1eb --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.js @@ -0,0 +1,165 @@ +"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 fs_1 = require("fs"); +var cliUtils_1 = require("../../../../FlowHelpers/1.0.0/cliUtils"); +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +var details = function () { return ({ + name: 'HandBrake Custom Arguments', + description: 'HandBrake Custom Arguments', + style: { + borderColor: 'green', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'customArguments', + type: 'string', + defaultValue: '-Z "Fast 1080p30" --all-subtitles', + inputUI: { + type: 'text', + }, + tooltip: 'Specify HandBrake arguments', + }, + { + name: 'jsonPreset', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Paste a HandBrake JSON preset here. Leave blank to disable.', + }, + { + name: 'container', + type: 'string', + defaultValue: 'mkv', + inputUI: { + type: 'dropdown', + options: [ + 'mkv', + 'mp4', + 'm4v', + 'avi', + 'mov', + 'mpg', + 'mpeg', + ], + }, + tooltip: 'Specify HandBrake arguments', + }, + ], + 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, customArguments, container, outputFilePath, presetString, cliArgs, presetPath, preset, 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); + customArguments = String(args.inputs.customArguments); + container = String(args.inputs.container); + outputFilePath = "".concat(args.workDir, "/tempFile_").concat(new Date().getTime(), ".").concat(container); + presetString = String(args.inputs.jsonPreset); + cliArgs = [ + '-i', + "".concat(args.inputFileObj._id), + '-o', + "".concat(outputFilePath), + ]; + presetPath = "".concat(args.workDir, "/preset.json"); + if (!(presetString.trim() !== '')) return [3 /*break*/, 2]; + preset = JSON.parse(presetString); + return [4 /*yield*/, fs_1.promises.writeFile(presetPath, JSON.stringify(preset, null, 2))]; + case 1: + _a.sent(); + cliArgs.push('--preset-import-file'); + cliArgs.push(presetPath); + cliArgs.push('-Z'); + cliArgs.push(preset.PresetList[0].PresetName); + return [3 /*break*/, 3]; + case 2: + cliArgs.push.apply(cliArgs, args.deps.parseArgsStringToArgv(customArguments, '', '')); + _a.label = 3; + case 3: + args.updateWorker({ + CLIType: args.handbrakePath, + preset: cliArgs.join(' '), + }); + cli = new cliUtils_1.CLI({ + cli: args.handbrakePath, + spawnArgs: cliArgs, + spawnOpts: {}, + jobLog: args.jobLog, + outputFilePath: outputFilePath, + inputFileObj: args.inputFileObj, + logFullCliOutput: args.logFullCliOutput, + updateWorker: args.updateWorker, + }); + return [4 /*yield*/, cli.runCli()]; + case 4: + res = _a.sent(); + if (res.cliExitCode !== 0) { + args.jobLog('Running HandBrake failed'); + throw new Error('Running HandBrake failed'); + } + args.logOutcome('tSuc'); + return [2 /*return*/, { + outputFileObj: { + _id: outputFilePath, + }, + outputNumber: 1, + variables: args.variables, + }]; + } + }); +}); }; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/input/inputFile/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/input/inputFile/1.0.0/index.js index 7c04a72..6f3fd72 100644 --- a/FlowPlugins/CommunityFlowPlugins/input/inputFile/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/input/inputFile/1.0.0/index.js @@ -4,7 +4,7 @@ exports.plugin = exports.details = void 0; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ name: 'Input File', - description: 'Transcode a video file using ffmpeg. GPU transcoding will be used if possible.', + description: 'Start the flow with an input file', style: { borderColor: 'pink', }, diff --git a/FlowPlugins/CommunityFlowPlugins/tools/failFlow/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/failFlow/1.0.0/index.js new file mode 100644 index 0000000..2572c7f --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/tools/failFlow/1.0.0/index.js @@ -0,0 +1,31 @@ +"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: 'Fail Flow', + description: 'Force the flow to fail and be move to the error table', + style: { + borderColor: 'red', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faExclamationTriangle', + inputs: [], + outputs: [], +}); }; +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); + throw new Error('Forcing flow to fail!'); + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.js new file mode 100644 index 0000000..88560da --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.js @@ -0,0 +1,31 @@ +"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: 'Go To Flow', + description: 'Go to a different flow', + style: { + borderColor: 'red', + opacity: 0.5, + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faArrowRight', + inputs: [], + outputs: [], +}); }; +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/unpack/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.js similarity index 65% rename from FlowPlugins/CommunityFlowPlugins/tools/unpack/1.0.0/index.js rename to FlowPlugins/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.js index 366c525..d39a608 100644 --- a/FlowPlugins/CommunityFlowPlugins/tools/unpack/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.js @@ -37,35 +37,20 @@ var __generator = (this && this.__generator) || function (thisArg, body) { }; Object.defineProperty(exports, "__esModule", { value: true }); exports.plugin = exports.details = void 0; +var cliUtils_1 = require("../../../../FlowHelpers/1.0.0/cliUtils"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ - name: 'Unpack File', - description: 'Unpack a file', + name: 'Run MKVPropEdit', + description: 'Run MKVPropEdit on a file to update metadata which' + + ' FFmpeg doesn\'t typically update such as stream bitrate.', style: { borderColor: 'green', - opacity: 0.5, }, tags: '', isStartPlugin: false, sidebarPosition: -1, - icon: 'faArrowRight', - inputs: [ - { - name: 'target_codec', - type: 'string', - defaultValue: 'hevc', - inputUI: { - type: 'dropdown', - options: [ - 'hevc', - // 'vp9', - 'h264', - // 'vp8', - ], - }, - tooltip: 'Specify the codec to use', - }, - ], + icon: '', + inputs: [], outputs: [ { number: 1, @@ -74,46 +59,38 @@ 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 lib, cliArgs, cli, res; return __generator(this, function (_a) { switch (_a.label) { case 0: - fs = require('fs'); lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); - currentPath = args.inputFileObj._id; - newPath = getNewPath(args.originalLibraryFile._id, currentPath); - newPathTmp = "".concat(newPath, ".tmp"); - args.jobLog(JSON.stringify({ - currentPath: currentPath, - newPath: newPath, - newPathTmp: newPathTmp, - })); - return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })]; + cliArgs = [ + '--add-track-statistics-tags', + args.inputFileObj._id, + ]; + cli = new cliUtils_1.CLI({ + cli: args.mkvpropeditPath, + spawnArgs: cliArgs, + spawnOpts: {}, + jobLog: args.jobLog, + outputFilePath: '', + inputFileObj: args.inputFileObj, + logFullCliOutput: args.logFullCliOutput, + updateWorker: args.updateWorker, + }); + return [4 /*yield*/, cli.runCli()]; case 1: - _a.sent(); - fs.renameSync(currentPath, newPathTmp); - if (fs.existsSync(newPath)) { - fs.unlinkSync(newPath); + res = _a.sent(); + if (res.cliExitCode !== 0) { + args.jobLog('Running MKVPropEdit failed'); + throw new Error('Running MKVPropEdit failed'); } - return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })]; - case 2: - _a.sent(); - fs.renameSync(newPathTmp, newPath); return [2 /*return*/, { - outputFileObj: { - _id: newPath, - }, + outputFileObj: args.inputFileObj, outputNumber: 1, variables: args.variables, }]; diff --git a/FlowPlugins/CommunityFlowPlugins/tools/webRequest/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/tools/webRequest/1.0.0/index.js index 920a9b0..00f6e8b 100644 --- a/FlowPlugins/CommunityFlowPlugins/tools/webRequest/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/tools/webRequest/1.0.0/index.js @@ -1,40 +1,4 @@ "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 }] */ @@ -49,23 +13,7 @@ var details = function () { return ({ isStartPlugin: false, sidebarPosition: -1, icon: 'faArrowRight', - inputs: [ - { - name: 'target_codec', - type: 'string', - defaultValue: 'hevc', - inputUI: { - type: 'dropdown', - options: [ - 'hevc', - // 'vp9', - 'h264', - // 'vp8', - ], - }, - tooltip: 'Specify the codec to use', - }, - ], + inputs: [], outputs: [ { number: 1, @@ -74,50 +22,15 @@ 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; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - fs = require('fs'); - lib = require('../../../../../methods/lib')(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign - args.inputs = lib.loadDefaultValues(args.inputs, details); - currentPath = args.inputFileObj._id; - newPath = getNewPath(args.originalLibraryFile._id, currentPath); - newPathTmp = "".concat(newPath, ".tmp"); - args.jobLog(JSON.stringify({ - currentPath: currentPath, - newPath: newPath, - newPathTmp: newPathTmp, - })); - return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })]; - case 1: - _a.sent(); - fs.renameSync(currentPath, newPathTmp); - if (fs.existsSync(newPath)) { - fs.unlinkSync(newPath); - } - return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })]; - case 2: - _a.sent(); - fs.renameSync(newPathTmp, newPath); - return [2 /*return*/, { - outputFileObj: { - _id: newPath, - }, - outputNumber: 1, - variables: args.variables, - }]; - } - }); -}); }; +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/video/check10Bit/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/check10Bit/1.0.0/index.js new file mode 100644 index 0000000..d436e35 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/video/check10Bit/1.0.0/index.js @@ -0,0 +1,46 @@ +"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 10 Bit Video', + description: 'Check if a file is 10 bit video', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'File is 10 bit video', + }, + { + number: 2, + tooltip: 'File is not 10 bit video', + }, + ], +}); }; +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 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; + } + } + return { + outputFileObj: args.inputFileObj, + outputNumber: is10Bit ? 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 new file mode 100644 index 0000000..64f9bf9 --- /dev/null +++ b/FlowPlugins/CommunityFlowPlugins/video/checkVideoBitrate/1.0.0/index.js @@ -0,0 +1,93 @@ +"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 Bitrate', + description: 'Check if video bitrate is within a specific range', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + 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 _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 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; + } + 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.BitRate >= greaterThanBits && stream.BitRate <= lessThanBits) { + isWithinRange = true; + } + } + }); + } + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +exports.plugin = plugin; diff --git a/FlowPlugins/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.js index a53aa54..e245b74 100644 --- a/FlowPlugins/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.js @@ -47,12 +47,13 @@ var plugin = function (args) { // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); var hasCodec = false; - // @ts-expect-error type - args.inputFileObj.ffProbeData.streams.forEach(function (stream) { - if (stream.codec_type === 'video' && stream.codec_name === args.inputs.codec) { - hasCodec = true; - } - }); + if (args.inputFileObj.ffProbeData.streams) { + args.inputFileObj.ffProbeData.streams.forEach(function (stream) { + if (stream.codec_type === 'video' && stream.codec_name === args.inputs.codec) { + hasCodec = true; + } + }); + } return { outputFileObj: args.inputFileObj, outputNumber: hasCodec ? 1 : 2, diff --git a/FlowPlugins/CommunityFlowTemplates/video/basicVideo.js b/FlowPlugins/CommunityFlowTemplates/video/basicVideo.js index 614ba97..1dd64af 100644 --- a/FlowPlugins/CommunityFlowTemplates/video/basicVideo.js +++ b/FlowPlugins/CommunityFlowTemplates/video/basicVideo.js @@ -1,198 +1,130 @@ "use strict"; var details = function () { return ({ - name: 'Basic Video Template', - description: 'Basic Video Template', + name: 'Basic HEVC Video Flow', + description: 'Flow description', tags: 'video', flowPlugins: [ { - id: 'nr55HwObs', - version: '1.0.0', - pluginName: 'inputFile', - inputsDB: {}, - position: { - x: 371.99540048613267, - y: -463.08388975391864, - }, - data: { - label: 'Input File', - }, + name: 'Input File', sourceRepo: 'Community', - }, - { - id: 'aDDcNO50Q', + pluginName: 'inputFile', version: '1.0.0', - pluginName: 'checkFileMedium', - inputsDB: {}, + id: 'pE6rU7gkW', position: { - x: 529.392455443893, - y: -349.448086326927, + x: 758.5809635618224, + y: 117.19206188888086, }, - data: { - label: 'Check File Medium', - }, - sourceRepo: 'Community', }, { - id: 'dBwjiWjfA', + name: 'Check if hevc', + sourceRepo: 'Community', + pluginName: 'checkVideoCodec', version: '1.0.0', - pluginName: 'replaceOriginalFile', - inputsDB: {}, + id: '91b7IrsEc', position: { - x: 517.8566785616271, - y: 145.89446592709146, - }, - data: { - label: 'Replace Original File', + x: 672.4549563302081, + y: 253.11148102973914, }, - sourceRepo: 'Community', }, { - id: '1m231hS5K', + name: 'Start', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandStart', version: '1.0.0', - pluginName: 'ffmpegCommandSetVideoEncoder', - inputsDB: {}, + id: '4Swd6qzvc', position: { - x: 104.71582826971436, - y: -44.833187887976514, - }, - data: { - label: 'Set Video Encoder', + x: 499.4549563302081, + y: 367.1114810297392, }, - sourceRepo: 'Community', }, { - id: 'iYEwAG4rk', + name: 'Execute', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandExecute', version: '1.0.0', - pluginName: 'ffmpegCommandStart', - inputsDB: {}, + id: '450g167D8', position: { - x: 138.43458422405857, - y: -149.36438875566702, - }, - data: { - label: 'Start', + x: 496.4549563302081, + y: 653.1114810297393, }, - sourceRepo: 'Community', }, { - id: 'staFGJ7lQ', + name: 'Set Video Encoder', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVideoEncoder', version: '1.0.0', - pluginName: 'ffmpegCommandExecute', - inputsDB: {}, + id: '8B_6pRd_U', position: { - x: 104.4813203353468, - y: 63.43024405079595, - }, - data: { - label: 'Execute', + x: 498.4549563302081, + y: 527.1114810297393, }, - sourceRepo: 'Community', }, { - id: '3sQBco3U6', + name: 'Replace Original File', + sourceRepo: 'Community', + pluginName: 'replaceOriginalFile', version: '1.0.0', - pluginName: 'checkVideoCodec', - inputsDB: {}, + id: '4fkfOyR3l', position: { - x: 252.92777615144564, - y: -274.3710285987198, + x: 820.4549563302082, + y: 742.2114810297393, }, - data: { - label: 'Check if HEVC', - }, - sourceRepo: 'Community', }, ], flowEdges: [ { - source: 'nr55HwObs', + source: 'pE6rU7gkW', sourceHandle: '1', - target: 'aDDcNO50Q', + target: '91b7IrsEc', targetHandle: null, + id: 'HhF4rw2DZ', animated: true, type: 'smoothstep', - id: 'reactflow__edge-nr55HwObs1-aDDcNO50Q', }, { - source: 'aDDcNO50Q', - sourceHandle: '3', - target: 'dBwjiWjfA', - targetHandle: null, - animated: true, - type: 'smoothstep', - id: 'reactflow__edge-aDDcNO50Q3-dBwjiWjfA', - }, - { - source: 'aDDcNO50Q', + source: '91b7IrsEc', sourceHandle: '2', - target: 'dBwjiWjfA', + target: '4Swd6qzvc', targetHandle: null, + id: 'jJizyFUcr', animated: true, type: 'smoothstep', - id: 'reactflow__edge-aDDcNO50Q2-dBwjiWjfA', }, { - source: 'iYEwAG4rk', + source: '4Swd6qzvc', sourceHandle: '1', - target: '1m231hS5K', + target: '8B_6pRd_U', targetHandle: null, + id: '3Df7Xoy93', animated: true, type: 'smoothstep', - id: 'reactflow__edge-iYEwAG4rk1-1m231hS5K', }, { - source: '1m231hS5K', + source: '8B_6pRd_U', sourceHandle: '1', - target: 'staFGJ7lQ', + target: '450g167D8', targetHandle: null, + id: 'BQerEKase', animated: true, type: 'smoothstep', - id: 'reactflow__edge-1m231hS5K1-staFGJ7lQ', }, { - source: 'staFGJ7lQ', - sourceHandle: '2', - target: 'dBwjiWjfA', - targetHandle: null, - animated: true, - type: 'smoothstep', - id: 'reactflow__edge-staFGJ7lQ2-dBwjiWjfA', - }, - { - source: 'staFGJ7lQ', - sourceHandle: '1', - target: 'dBwjiWjfA', - targetHandle: null, - animated: true, - type: 'smoothstep', - id: 'reactflow__edge-staFGJ7lQ1-dBwjiWjfA', - }, - { - source: 'aDDcNO50Q', + source: '450g167D8', sourceHandle: '1', - target: '3sQBco3U6', + target: '4fkfOyR3l', targetHandle: null, + id: 'rE5Dsh9KM', animated: true, type: 'smoothstep', - id: 'reactflow__edge-aDDcNO50Q1-3sQBco3U6', }, { - source: '3sQBco3U6', + source: '91b7IrsEc', sourceHandle: '1', - target: 'dBwjiWjfA', - targetHandle: null, - animated: true, - type: 'smoothstep', - id: 'reactflow__edge-3sQBco3U61-dBwjiWjfA', - }, - { - source: '3sQBco3U6', - sourceHandle: '2', - target: 'iYEwAG4rk', + target: '4fkfOyR3l', targetHandle: null, + id: 'W2nVG7ts5', animated: true, type: 'smoothstep', - id: 'reactflow__edge-3sQBco3U62-iYEwAG4rk', }, ], }); }; diff --git a/FlowPlugins/FlowHelpers/1.0.0/utils.js b/FlowPlugins/FlowHelpers/1.0.0/cliUtils.js similarity index 96% rename from FlowPlugins/FlowHelpers/1.0.0/utils.js rename to FlowPlugins/FlowHelpers/1.0.0/cliUtils.js index 84fdf96..392e868 100644 --- a/FlowPlugins/FlowHelpers/1.0.0/utils.js +++ b/FlowPlugins/FlowHelpers/1.0.0/cliUtils.js @@ -225,20 +225,20 @@ var CLI = /** @class */ (function () { childProcess = require('child_process'); errorLogFull = []; // eslint-disable-next-line no-console - console.log("Running ".concat(this.config.cli, " ").concat(this.config.spawnArgs.join(' '))); + this.config.jobLog("Running ".concat(this.config.cli, " ").concat(this.config.spawnArgs.join(' '))); return [4 /*yield*/, new Promise(function (resolve) { try { var opts = _this.config.spawnOpts || {}; var thread = childProcess.spawn(_this.config.cli, _this.config.spawnArgs, opts); thread.stdout.on('data', function (data) { // eslint-disable-next-line no-console - console.log(data.toString()); + // console.log(data.toString()); errorLogFull.push(data.toString()); _this.parseOutput(data); }); thread.stderr.on('data', function (data) { // eslint-disable-next-line no-console - console.log(data.toString()); + // console.log(data.toString()); errorLogFull.push(data.toString()); _this.parseOutput(data); }); @@ -253,7 +253,7 @@ var CLI = /** @class */ (function () { thread.on('close', function (code) { if (code !== 0) { // eslint-disable-next-line no-console - console.log(code, 'FFmpeg error'); + console.log(code, 'CLI error'); } resolve(code); }); @@ -267,6 +267,9 @@ var CLI = /** @class */ (function () { })]; case 1: cliExitCode = _a.sent(); + if (!this.config.logFullCliOutput) { + this.config.jobLog(errorLogFull.slice(-1000).join('')); + } return [2 /*return*/, { cliExitCode: cliExitCode, errorLogFull: errorLogFull, diff --git a/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js b/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js new file mode 100644 index 0000000..d756ba8 --- /dev/null +++ b/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getFfType = exports.getFileName = exports.getContainer = void 0; +var getContainer = function (filePath) { + var parts = filePath.split('.'); + return parts[parts.length - 1]; +}; +exports.getContainer = getContainer; +var getFileName = function (filePath) { + var parts = filePath.split('/'); + var fileNameAndContainer = parts[parts.length - 1]; + var parts2 = fileNameAndContainer.split('.'); + return parts2[0]; +}; +exports.getFileName = getFileName; +var getFfType = function (codecType) { return (codecType === 'video' ? 'v' : 'a'); }; +exports.getFfType = getFfType; diff --git a/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.js b/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.js new file mode 100644 index 0000000..44fad7b --- /dev/null +++ b/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.js @@ -0,0 +1,325 @@ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +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.getEncoder = exports.getBestNvencDevice = exports.hasEncoder = void 0; +var hasEncoder = function (_a) { + var ffmpegPath = _a.ffmpegPath, encoder = _a.encoder, inputArgs = _a.inputArgs, filter = _a.filter; + return __awaiter(void 0, void 0, void 0, function () { + var exec, isEnabled, err_1; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + exec = require('child_process').exec; + isEnabled = false; + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [4 /*yield*/, new Promise(function (resolve) { + 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"); + exec(command, function ( + // eslint-disable-next-line + error) { + if (error) { + resolve(false); + return; + } + resolve(true); + }); + })]; + case 2: + isEnabled = _b.sent(); + return [3 /*break*/, 4]; + case 3: + err_1 = _b.sent(); + // eslint-disable-next-line no-console + console.log(err_1); + return [3 /*break*/, 4]; + case 4: return [2 /*return*/, isEnabled]; + } + }); + }); +}; +exports.hasEncoder = hasEncoder; +// credit to UNCode101 for this +var getBestNvencDevice = function (_a) { + var args = _a.args, nvencDevice = _a.nvencDevice; + var execSync = require('child_process').execSync; + var gpu_num = -1; + var lowest_gpu_util = 100000; + var result_util = 0; + var gpu_count = -1; + var gpu_names = ''; + var gpus_to_exclude = []; + // inputs.exclude_gpus === '' ? [] : inputs.exclude_gpus.split(',').map(Number); + try { + gpu_names = execSync('nvidia-smi --query-gpu=name --format=csv,noheader'); + gpu_names = gpu_names.toString().trim(); + var gpu_namesArr = gpu_names.split(/\r?\n/); + /* When nvidia-smi returns an error it contains 'nvidia-smi' in the error + Example: Linux: nvidia-smi: command not found + Windows: 'nvidia-smi' is not recognized as an internal or external command, + operable program or batch file. */ + if (!gpu_namesArr[0].includes('nvidia-smi')) { + gpu_count = gpu_namesArr.length; + } + } + catch (error) { + args.jobLog('Error in reading nvidia-smi output! \n'); + } + if (gpu_count > 0) { + for (var gpui = 0; gpui < gpu_count; gpui += 1) { + // Check if GPU # is in GPUs to exclude + if (gpus_to_exclude.includes(String(gpui))) { + args.jobLog("GPU ".concat(gpui, ": ").concat(gpu_names[gpui], " is in exclusion list, will not be used!\n")); + } + else { + try { + var cmd_gpu = "nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits -i ".concat(gpui); + result_util = parseInt(execSync(cmd_gpu), 10); + if (!Number.isNaN(result_util)) { // != "No devices were found") { + args.jobLog("GPU ".concat(gpui, " : Utilization ").concat(result_util, "%\n")); + if (result_util < lowest_gpu_util) { + gpu_num = gpui; + lowest_gpu_util = result_util; + } + } + } + catch (error) { + args.jobLog("Error in reading GPU ".concat(gpui, " Utilization\nError: ").concat(error, "\n")); + } + } + } + } + if (gpu_num >= 0) { + // eslint-disable-next-line no-param-reassign + nvencDevice.inputArgs.push('-hwaccel_device', "".concat(gpu_num)); + // eslint-disable-next-line no-param-reassign + nvencDevice.outputArgs.push('-gpu', "".concat(gpu_num)); + } + return nvencDevice; +}; +exports.getBestNvencDevice = getBestNvencDevice; +var encoderFilter = function (encoder, targetCodec) { + if (targetCodec === 'hevc' && (encoder.includes('hevc') || encoder.includes('h265'))) { + return true; + } + if (targetCodec === 'h264' && encoder.includes('h264')) { + return true; + } + return false; +}; +var getEncoder = function (_a) { + var targetCodec = _a.targetCodec, hardwareEncoding = _a.hardwareEncoding, args = _a.args; + return __awaiter(void 0, void 0, void 0, function () { + var gpuEncoders, filteredGpuEncoders, _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]; + gpuEncoders = [ + { + encoder: 'hevc_nvenc', + enabled: false, + inputArgs: [ + '-hwaccel', + 'cuda', + ], + outputArgs: [], + filter: '', + }, + { + encoder: 'hevc_amf', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'hevc_vaapi', + inputArgs: [ + '-hwaccel', + 'vaapi', + '-hwaccel_device', + '/dev/dri/renderD128', + '-hwaccel_output_format', + 'vaapi', + ], + outputArgs: [], + enabled: false, + filter: '-vf format=nv12,hwupload', + }, + { + encoder: 'hevc_qsv', + enabled: false, + inputArgs: [ + '-hwaccel', + 'qsv', + ], + outputArgs: [], + filter: '', + }, + { + encoder: 'hevc_videotoolbox', + enabled: false, + inputArgs: [ + '-hwaccel', + 'videotoolbox', + ], + outputArgs: [], + filter: '', + }, + { + encoder: 'h264_nvenc', + enabled: false, + inputArgs: [ + '-hwaccel', + 'cuda', + ], + outputArgs: [], + filter: '', + }, + { + encoder: 'h264_amf', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'h264_qsv', + enabled: false, + inputArgs: [ + '-hwaccel', + 'qsv', + ], + outputArgs: [], + filter: '', + }, + { + encoder: 'h264_videotoolbox', + enabled: false, + inputArgs: [ + '-hwaccel', + 'videotoolbox', + ], + outputArgs: [], + filter: '', + }, + ]; + filteredGpuEncoders = gpuEncoders.filter(function (device) { return encoderFilter(device.encoder, targetCodec); }); + _i = 0, filteredGpuEncoders_1 = filteredGpuEncoders; + _c.label = 1; + case 1: + if (!(_i < filteredGpuEncoders_1.length)) return [3 /*break*/, 4]; + gpuEncoder = filteredGpuEncoders_1[_i]; + // eslint-disable-next-line no-await-in-loop + _b = gpuEncoder; + return [4 /*yield*/, (0, exports.hasEncoder)({ + ffmpegPath: args.ffmpegPath, + encoder: gpuEncoder.encoder, + inputArgs: gpuEncoder.inputArgs, + filter: gpuEncoder.filter, + })]; + case 2: + // eslint-disable-next-line no-await-in-loop + _b.enabled = _c.sent(); + _c.label = 3; + case 3: + _i++; + return [3 /*break*/, 1]; + case 4: + enabledDevices = gpuEncoders.filter(function (device) { return device.enabled === true; }); + 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*/, { + encoder: enabledDevices[0].encoder, + inputArgs: enabledDevices[0].inputArgs, + outputArgs: enabledDevices[0].outputArgs, + isGpu: true, + }]; + } + _c.label = 5; + case 5: + if (targetCodec === 'hevc') { + return [2 /*return*/, { + encoder: 'libx265', + inputArgs: [], + outputArgs: [], + isGpu: false, + }]; + } + if (targetCodec === 'h264') { + return [2 /*return*/, { + encoder: 'libx264', + inputArgs: [], + outputArgs: [], + isGpu: false, + }]; + } + return [2 /*return*/, { + encoder: targetCodec, + inputArgs: [], + outputArgs: [], + isGpu: false, + }]; + } + }); + }); +}; +exports.getEncoder = getEncoder; diff --git a/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.test.js b/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.test.js new file mode 100644 index 0000000..d3c0a49 --- /dev/null +++ b/FlowPlugins/FlowHelpers/1.0.0/hardwareUtils.test.js @@ -0,0 +1,67 @@ +"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 }); +var hardwareUtils_1 = require("./hardwareUtils"); +var run = function () { return __awaiter(void 0, void 0, void 0, function () { + var encoderProperties; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, (0, hardwareUtils_1.getEncoder)({ + targetCodec: 'h264', + hardwareEncoding: true, + // @ts-expect-error type + args: { + workerType: 'transcodegpu', + ffmpegPath: 'ffmpeg', + jobLog: function (t) { + // eslint-disable-next-line no-console + console.log(t); + }, + }, + })]; + case 1: + encoderProperties = _a.sent(); + // eslint-disable-next-line no-console + console.log({ + encoderProperties: encoderProperties, + }); + return [2 /*return*/]; + } + }); +}); }; +void run(); diff --git a/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.ts new file mode 100644 index 0000000..b7f7e2f --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicFilterPlugin/1.0.0/index.ts @@ -0,0 +1,115 @@ +import { getContainer } 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: 'Run Classic Filter Plugin', + description: 'Run one of Tdarr\'s classic plugins that has Operation: Filter', + style: { + borderColor: 'orange', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'pluginSourceId', + type: 'string', + defaultValue: 'Community:Tdarr_Plugin_00td_filter_by_codec', + inputUI: { + type: 'dropdown', + options: [], + }, + tooltip: 'Specify the classic plugin ID', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File met conditions, would traditionally continue to next plugin in plugin stack', + }, + { + number: 2, + tooltip: 'File did not meet conditions, would traditionally break out of plugin stack', + }, + ], +}); + +// 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); + + args.jobLog(JSON.stringify(result, null, 2)); + + const outputNumber = result.processFile ? 1 : 2; + + return { + outputFileObj: args.inputFileObj, + outputNumber, + variables: args.variables, + }; +}; + +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.ts new file mode 100644 index 0000000..c70489a --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/classic/runClassicTranscodePlugin/1.0.0/index.ts @@ -0,0 +1,237 @@ +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'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Run Classic Transcode Plugin', + description: 'Run one of Tdarr\'s classic plugins that has Operation: Transcode', + style: { + borderColor: 'green', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'pluginSourceId', + type: 'string', + defaultValue: 'Community:Tdarr_Plugin_MC93_Migz1FFMPEG', + inputUI: { + type: 'dropdown', + options: [], + }, + tooltip: 'Specify the classic plugin ID', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +const replaceContainer = (filePath:string, container:string): string => { + const parts = filePath.split('.'); + parts[parts.length - 1] = container.split('.').join(''); + return parts.join('.'); +}; + +// 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: 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); + + args.jobLog(JSON.stringify(result, null, 2)); + + // --- Backwards compatibility------------ + if (result.handBrakeMode) { + result.handbrakeMode = result.handBrakeMode; + } + + if (result.FFmpegMode) { + result.ffmpegMode = result.FFmpegMode; + } + //---------------------------------------- + + if (result.ffmpegMode) { + result.cliToUse = 'ffmpeg'; + } else if (result.handbrakeMode) { + result.cliToUse = 'handbrake'; + } else if (typeof result?.custom?.cliPath === 'string') { + const { cliPath } = result.custom; + if (cliPath.toLowerCase().includes('ffmpeg')) { + result.cliToUse = 'ffmpeg'; + } else if (cliPath.toLowerCase().includes('handbrake')) { + result.cliToUse = 'handbrake'; + } else if (cliPath.toLowerCase().includes('editready')) { + result.cliToUse = 'editready'; + } else if (cliPath.toLowerCase().includes('av1an')) { + result.cliToUse = 'av1an'; + } + } + + result.workerLog = result.transcodeSettingsLog; + args.jobLog(JSON.stringify(result, null, 2)); + + if (result.error) { + throw new Error(`Plugin ${absolutePath} failed: ${result.error}`); + } if (result.processFile !== true) { + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; + } + + const customArgs = result?.custom?.args; + const isCustomConfig = (Array.isArray(customArgs) && customArgs.length > 0) + || (typeof customArgs === 'string' && customArgs.length > 0); + + if (!isCustomConfig) { + cacheFilePath = replaceContainer(cacheFilePath, result.container); + } else { + cacheFilePath = result.custom.outputPath; + } + + let presetSplit; + if (result.preset.includes('')) { + presetSplit = result.preset.split(''); + } else { + presetSplit = result.preset.split(','); + } + + let workerCommand: string[] = []; + let cliPath = ''; + + if (isCustomConfig) { + cliPath = result?.custom?.cliPath; + + if (Array.isArray(customArgs)) { + workerCommand = customArgs; + } else { + workerCommand = [ + ...args.deps.parseArgsStringToArgv(customArgs, '', ''), + ]; + } + } else { + // working on windows with '` and spaces + // working on unix with ' + switch (true) { + case result.cliToUse === 'handbrake': + workerCommand = [ + '-i', + `${args.inputFileObj._id}`, + '-o', + `${cacheFilePath}`, + ...args.deps.parseArgsStringToArgv(result.preset, '', ''), + ]; + + cliPath = `${args.handbrakePath}`; + break; + + case result.cliToUse === 'ffmpeg': + workerCommand = [ + ...args.deps.parseArgsStringToArgv(presetSplit[0], '', ''), + '-i', + `${args.inputFileObj._id}`, + ...args.deps.parseArgsStringToArgv(presetSplit[1], '', ''), + `${cacheFilePath}`, + ]; + cliPath = `${args.ffmpegPath}`; + break; + default: + } + } + + const cli = new CLI({ + cli: cliPath, + spawnArgs: workerCommand, + spawnOpts: {}, + jobLog: args.jobLog, + outputFilePath: cacheFilePath, + inputFileObj: args.inputFileObj, + logFullCliOutput: args.logFullCliOutput, + updateWorker: args.updateWorker, + }); + + const res = await cli.runCli(); + + if (res.cliExitCode !== 0) { + args.jobLog(`Running ${cliPath} failed`); + throw new Error(`Running ${cliPath} failed`); + } + + args.logOutcome('tSuc'); + + return { + outputFileObj: { + _id: cacheFilePath, + }, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.ts index 0a8302b..ba54fd3 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.ts @@ -10,7 +10,6 @@ const details = () :IpluginDetails => ({ description: 'Set 10 Bit Video', style: { borderColor: '#6efefc', - opacity: 0.5, }, tags: 'video', isStartPlugin: false, @@ -31,6 +30,13 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); + for (let i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { + const stream = args.variables.ffmpegCommand.streams[i]; + if (stream.codec_type === 'video') { + stream.outputArgs.push('-pix_fmt:v:{outputTypeIndex}', 'p010le', '-profile:v:{outputTypeIndex}', 'main10'); + } + } + return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.ts index a2f06f2..0e3c32a 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandExecute/1.0.0/index.ts @@ -1,9 +1,11 @@ import { + IffmpegCommandStream, IpluginDetails, IpluginInputArgs, IpluginOutputArgs, } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; -import { CLI } from '../../../../FlowHelpers/1.0.0/utils'; +import { CLI } from '../../../../FlowHelpers/1.0.0/cliUtils'; +import { getContainer } from '../../../../FlowHelpers/1.0.0/fileUtils'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = (): IpluginDetails => ({ @@ -21,24 +23,41 @@ const details = (): IpluginDetails => ({ outputs: [ { number: 1, - tooltip: 'File is 480p', - }, - { - number: 2, - tooltip: 'File is 576p', + tooltip: 'Continue to next plugin', }, ], }); -const getEncoder = (codec: string) => { - switch (codec) { - case 'h264': - return 'libx264'; - case 'hevc': - return 'libx265'; - default: - return codec; +const getOuputStreamIndex = (streams: IffmpegCommandStream[], stream: IffmpegCommandStream): number => { + let index = -1; + + for (let idx = 0; idx < streams.length; idx += 1) { + if (!stream.removed) { + index += 1; + } + + if (streams[idx].index === stream.index) { + break; + } } + + return index; +}; + +const getOuputStreamTypeIndex = (streams: IffmpegCommandStream[], stream: IffmpegCommandStream): number => { + let index = -1; + + for (let idx = 0; idx < streams.length; idx += 1) { + if (!stream.removed && streams[idx].codec_type === stream.codec_type) { + index += 1; + } + + if (streams[idx].index === stream.index) { + break; + } + } + + return index; }; // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -53,25 +72,48 @@ const plugin = async (args: IpluginInputArgs): Promise => { cliArgs.push('-i'); cliArgs.push(args.inputFileObj._id); - let shouldProcess = false; + const inputArgs: string[] = []; + let { shouldProcess, streams } = args.variables.ffmpegCommand; - // @ts-expect-error type - args.variables.ffmpegCommand.streams.forEach((stream) => { - if (!stream.removed) { - cliArgs.push('-map'); - cliArgs.push(`0:${stream.index}`); - cliArgs.push(`-c:${stream.index}`); - - args.jobLog(JSON.stringify({ stream })); - if (args.inputs.forceProcess || stream.codec_name !== stream.targetCodec) { - shouldProcess = true; - cliArgs.push(getEncoder(stream.targetCodec)); - } else { - cliArgs.push('copy'); - } + streams = streams.filter((stream) => { + if (stream.removed) { + shouldProcess = true; } + return !stream.removed; }); + if (getContainer(args.inputFileObj._id) !== args.variables.ffmpegCommand.container) { + shouldProcess = true; + } + + for (let i = 0; i < streams.length; i += 1) { + const stream = streams[i]; + + stream.outputArgs = stream.outputArgs.map((arg) => { + if (arg.includes('{outputIndex}')) { + // eslint-disable-next-line no-param-reassign + arg = arg.replace('{outputIndex}', String(getOuputStreamIndex(streams, stream))); + } + + if (arg.includes('{outputTypeIndex}')) { + // eslint-disable-next-line no-param-reassign + arg = arg.replace('{outputTypeIndex}', String(getOuputStreamTypeIndex(streams, stream))); + } + + return arg; + }); + + cliArgs.push(...stream.mapArgs); + + if (stream.outputArgs.length === 0) { + cliArgs.push(`-c:${getOuputStreamIndex(streams, stream)}`, 'copy'); + } else { + cliArgs.push(...stream.outputArgs); + } + + inputArgs.push(...stream.inputArgs); + } + if (!shouldProcess) { args.jobLog('No need to process file, already as required'); return { @@ -81,12 +123,11 @@ const plugin = async (args: IpluginInputArgs): Promise => { }; } - // @ts-expect-error type - const outputFilePath = `${args.workDir}/tempFile.${args.variables.ffmpegCommand.container}`; - cliArgs.push(outputFilePath); + const idx = cliArgs.indexOf('-i'); + cliArgs.splice(idx, 0, ...inputArgs); - // @ts-expect-error type - args.deps.fsextra.ensureDirSync(args.workDir); + const outputFilePath = `${args.workDir}/tempFile_${new Date().getTime()}.${args.variables.ffmpegCommand.container}`; + cliArgs.push(outputFilePath); args.jobLog('Processing file'); args.jobLog(JSON.stringify({ @@ -94,6 +135,11 @@ const plugin = async (args: IpluginInputArgs): Promise => { outputFilePath, })); + args.updateWorker({ + CLIType: args.ffmpegPath, + preset: cliArgs.join(' '), + }); + const cli = new CLI({ cli: args.ffmpegPath, spawnArgs: cliArgs, @@ -107,10 +153,6 @@ const plugin = async (args: IpluginInputArgs): Promise => { const res = await cli.runCli(); - if (!args.logFullCliOutput) { - args.jobLog(res.errorLogFull.slice(-1000).join('')); - } - if (res.cliExitCode !== 0) { args.jobLog('Running FFmpeg failed'); throw new Error('FFmpeg failed'); diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.ts index 7252de9..dbf9aa9 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.ts @@ -33,7 +33,6 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); - // @ts-expect-error type args.variables.ffmpegCommand.streams.forEach((stream) => { if (stream.codec_type === 'data') { stream.removed = true; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.ts index 4212b06..60ac7a8 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.ts @@ -32,7 +32,6 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); - // @ts-expect-error type args.variables.ffmpegCommand.streams.forEach((stream) => { if (stream.codec_type === 'subtitle') { stream.removed = true; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.ts index 6446a1d..153ae14 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRorderStreams/1.0.0/index.ts @@ -1,22 +1,93 @@ import { + IffmpegCommandStream, IpluginDetails, IpluginInputArgs, IpluginOutputArgs, } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; +import { Istreams } from '../../../../FlowHelpers/1.0.0/interfaces/synced/IFileObject'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ -const details = () :IpluginDetails => ({ - name: 'Set Video Bitrate', - description: 'Set Video Bitrate', +const details = (): IpluginDetails => ({ + name: 'Reorder Streams', + description: 'Reorder Streams', style: { borderColor: '#6efefc', - opacity: 0.5, }, tags: 'video', isStartPlugin: false, sidebarPosition: -1, icon: '', - inputs: [], + inputs: [ + { + name: 'processOrder', + type: 'string', + defaultValue: 'codecs,channels,languages,streamTypes', + inputUI: { + type: 'text', + }, + tooltip: + `Specify the process order. +For example, if 'languages' is first, the streams will be ordered based on that first. +So put the most important properties last. +The default order is suitable for most people. + + \\nExample:\\n + codecs,channels,languages,streamTypes + `, + }, + { + name: 'languages', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: + `Specify the language tags order, separated by commas. Leave blank to disable. + \\nExample:\\n + eng,fre + `, + }, + { + name: 'channels', + type: 'string', + defaultValue: '7.1,5.1,2,1', + inputUI: { + type: 'text', + }, + tooltip: + `Specify the channels order, separated by commas. Leave blank to disable. + + \\nExample:\\n + 7.1,5.1,2,1`, + }, + { + name: 'codecs', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: + `Specify the codec order, separated by commas. Leave blank to disable. + + \\nExample:\\n + aac,ac3`, + }, + { + name: 'streamTypes', + type: 'string', + defaultValue: 'video,audio,subtitle', + inputUI: { + type: 'text', + }, + tooltip: + `Specify the streamTypes order, separated by commas. Leave blank to disable. + \\nExample:\\n + video,audio,subtitle + `, + }, + ], outputs: [ { number: 1, @@ -26,11 +97,125 @@ const details = () :IpluginDetails => ({ }); // eslint-disable-next-line @typescript-eslint/no-unused-vars -const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { +const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { const lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); + let streams: IffmpegCommandStream[] = JSON.parse(JSON.stringify(args.variables.ffmpegCommand.streams)); + + const originalStreams = JSON.stringify(streams); + + streams.forEach((stream, index) => { + // eslint-disable-next-line no-param-reassign + stream.typeIndex = index; + }); + + const sortStreams = (sortType: { + inputs: string, + getValue: (stream: Istreams) => string, + }) => { + const items = sortType.inputs.split(','); + items.reverse(); + for (let i = 0; i < items.length; i += 1) { + const matchedStreams = []; + for (let j = 0; j < streams.length; j += 1) { + if (String(sortType.getValue(streams[j])) === String(items[i])) { + if ( + streams[j].codec_long_name + && ( + streams[j].codec_long_name.includes('image') + || streams[j].codec_name.includes('png') + ) + ) { + // do nothing, ffmpeg bug, doesn't move image streams + } else { + matchedStreams.push(streams[j]); + streams.splice(j, 1); + j -= 1; + } + } + } + streams = matchedStreams.concat(streams); + } + }; + + const processOrder = String(args.inputs.processOrder); + + const { + languages, codecs, channels, streamTypes, + } = args.inputs; + + const sortTypes:{ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any, + } = { + languages: { + getValue: (stream: Istreams) => { + if (stream?.tags?.language) { + return stream.tags.language; + } + + return ''; + }, + inputs: languages, + }, + codecs: { + getValue: (stream: Istreams) => { + try { + return stream.codec_name; + } catch (err) { + // err + } + return ''; + }, + inputs: codecs, + }, + channels: { + getValue: (stream: Istreams) => { + const chanMap:{ + [key: number]: string + } = { + 8: '7.1', + 6: '5.1', + 2: '2', + 1: '1', + }; + + if (stream?.channels && chanMap[stream.channels]) { + return chanMap[stream.channels]; + } + + return ''; + }, + inputs: channels, + }, + streamTypes: { + getValue: (stream:Istreams) => { + if (stream.codec_type) { + return stream.codec_type; + } + return ''; + }, + inputs: streamTypes, + }, + }; + + const processOrderArr = processOrder.split(','); + + for (let k = 0; k < processOrderArr.length; k += 1) { + if (sortTypes[processOrderArr[k]] && sortTypes[processOrderArr[k]].inputs) { + sortStreams(sortTypes[processOrderArr[k]]); + } + } + + if (JSON.stringify(streams) !== originalStreams) { + // eslint-disable-next-line no-param-reassign + args.variables.ffmpegCommand.shouldProcess = true; + // eslint-disable-next-line no-param-reassign + args.variables.ffmpegCommand.streams = streams; + } + return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.ts index 3815891..15662d0 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetContainer/1.0.0/index.ts @@ -46,8 +46,7 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); - // @ts-expect-error type - args.variables.ffmpegCommand.container = args.inputs.container; + args.variables.ffmpegCommand.container = String(args.inputs.container); return { outputFileObj: args.inputFileObj, diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.ts new file mode 100644 index 0000000..3190f72 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVdeoResolution/1.0.0/index.ts @@ -0,0 +1,103 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Set Video Resolution', + description: 'Change video resolution', + style: { + borderColor: '#6efefc', + }, + tags: 'video', + isStartPlugin: false, + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'targetResolution', + type: 'string', + defaultValue: '1080p', + inputUI: { + type: 'dropdown', + options: [ + '480p', + '720p', + '1080p', + '1440p', + '4KUHD', + ], + }, + tooltip: 'Specify the codec to use', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +const getVfScale = ( + targetResolution: string, +):string[] => { + switch (targetResolution) { + case '480p': + return ['-vf', 'scale=720:-2']; + + case '576p': + return ['-vf', 'scale=720:-2']; + + case '720p': + return ['-vf', 'scale=1280:-2']; + + case '1080p': + return ['-vf', 'scale=1920:-2']; + + case '1440p': + return ['-vf', 'scale=2560:-2']; + + case '4KUHD': + return ['-vf', 'scale=3840:-2']; + + default: + return ['-vf', 'scale=1920:-2']; + } +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + for (let i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { + const stream = args.variables.ffmpegCommand.streams[i]; + + if (stream.codec_type === 'video') { + const targetResolution = String(args.inputs.targetResolution); + + if ( + targetResolution !== args.inputFileObj.video_resolution + ) { + // eslint-disable-next-line no-param-reassign + args.variables.ffmpegCommand.shouldProcess = true; + const scaleArgs = getVfScale(targetResolution); + stream.outputArgs.push(...scaleArgs); + } + } + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.ts index dca3703..a7c5481 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoBitrate/1.0.0/index.ts @@ -3,20 +3,30 @@ import { IpluginInputArgs, IpluginOutputArgs, } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; +import { getFfType } from '../../../../FlowHelpers/1.0.0/fileUtils'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = () :IpluginDetails => ({ - name: 'Reorder Streams', - description: 'Reorder Streams', + name: 'Set Video Bitrate', + description: 'Set Video Bitrate', style: { borderColor: '#6efefc', - opacity: 0.5, }, tags: 'video', isStartPlugin: false, sidebarPosition: -1, icon: '', - inputs: [], + inputs: [ + { + name: 'bitrate', + type: 'string', + defaultValue: '5000', + inputUI: { + type: 'text', + }, + tooltip: 'Specify bitrate in kbps', + }, + ], outputs: [ { number: 1, @@ -31,6 +41,13 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type === 'video') { + const ffType = getFfType(stream.codec_type); + stream.outputArgs.push(`-b:${ffType}:{outputTypeIndex}`, `${String(args.inputs.bitrate)}k`); + } + }); + return { outputFileObj: args.inputFileObj, outputNumber: 1, diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.ts index b2f8d09..1bba815 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoEncoder/1.0.0/index.ts @@ -1,5 +1,6 @@ /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +import { getEncoder } from '../../../../FlowHelpers/1.0.0/hardwareUtils'; import { IpluginDetails, IpluginInputArgs, @@ -7,7 +8,7 @@ import { } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; /* eslint-disable no-param-reassign */ -const details = () :IpluginDetails => ({ +const details = (): IpluginDetails => ({ name: 'Set Video Encoder', description: 'Set the video encoder for all streams', style: { @@ -19,7 +20,7 @@ const details = () :IpluginDetails => ({ icon: '', inputs: [ { - name: 'targetCodec', + name: 'outputCodec', type: 'string', defaultValue: 'hevc', inputUI: { @@ -33,6 +34,61 @@ const details = () :IpluginDetails => ({ }, tooltip: 'Specify the codec to use', }, + { + name: 'ffmpegPreset', + type: 'string', + defaultValue: 'fast', + inputUI: { + type: 'dropdown', + options: [ + 'veryslow', + 'slower', + 'slow', + 'medium', + 'fast', + 'faster', + 'veryfast', + 'superfast', + 'ultrafast', + ], + }, + tooltip: 'Specify the codec to use', + }, + { + name: 'ffmpegQuality', + type: 'number', + defaultValue: '25', + inputUI: { + type: 'text', + }, + tooltip: 'Specify the codec to use', + }, + { + name: 'hardwareEncoding', + type: 'boolean', + defaultValue: 'true', + inputUI: { + type: 'dropdown', + options: [ + 'false', + 'true', + ], + }, + tooltip: 'Specify whether to use hardware encoding if available', + }, + { + name: 'hardwareDecoding', + type: 'boolean', + defaultValue: 'true', + inputUI: { + type: 'dropdown', + options: [ + 'false', + 'true', + ], + }, + tooltip: 'Specify whether to use hardware decoding if available', + }, { name: 'forceEncoding', type: 'boolean', @@ -56,19 +112,59 @@ const details = () :IpluginDetails => ({ }); // eslint-disable-next-line @typescript-eslint/no-unused-vars -const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { +const plugin = async (args: IpluginInputArgs): Promise => { const lib = require('../../../../../methods/lib')(); // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign args.inputs = lib.loadDefaultValues(args.inputs, details); - // @ts-expect-error type - args.variables.ffmpegCommand.streams.forEach((stream) => { + const hardwareDecoding = args.inputs.hardwareDecoding === true; + args.variables.ffmpegCommand.hardwareDecoding = hardwareDecoding; + + for (let i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { + const stream = args.variables.ffmpegCommand.streams[i]; + if (stream.codec_type === 'video') { - // @ts-expect-error type - stream.targetCodec = args.inputs.targetCodec; - stream.forceEncoding = args.inputs.forceEncoding; + const targetCodec = String(args.inputs.outputCodec); + const ffmpegPreset = String(args.inputs.ffmpegPreset); + const ffmpegQuality = String(args.inputs.ffmpegQuality); + const forceEncoding = args.inputs.forceEncoding === true; + const hardwarEncoding = args.inputs.hardwareEncoding === true; + + if ( + forceEncoding + || stream.codec_name !== targetCodec + ) { + args.variables.ffmpegCommand.shouldProcess = true; + + // eslint-disable-next-line no-await-in-loop + const encoderProperties = await getEncoder({ + targetCodec, + hardwareEncoding: hardwarEncoding, + args, + }); + + stream.outputArgs.push('-c:{outputIndex}', encoderProperties.encoder); + + if (encoderProperties.isGpu) { + stream.outputArgs.push('-qp', ffmpegQuality); + } else { + stream.outputArgs.push('-crf', ffmpegQuality); + } + + if (ffmpegPreset) { + stream.outputArgs.push('-preset', ffmpegPreset); + } + + if (hardwareDecoding) { + stream.inputArgs.push(...encoderProperties.inputArgs); + } + + if (encoderProperties.outputArgs) { + stream.outputArgs.push(...encoderProperties.outputArgs); + } + } } - }); + } return { outputFileObj: args.inputFileObj, diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.ts index 1196c4e..83430cf 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStart/1.0.0/index.ts @@ -42,10 +42,18 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { streams: JSON.parse(JSON.stringify(args.inputFileObj.ffProbeData.streams)).map((stream:Istreams) => ({ ...stream, removed: false, - targetCodec: stream.codec_name, - args: [], + mapArgs: [ + '-map', + `0:${stream.index}`, + ], + inputArgs: [], + outputArgs: [], })), container, + hardwareDecoding: false, + shouldProcess: false, + overallInputArguments: [], + overallOuputArguments: [], }; args.variables.ffmpegCommand = ffmpegCommand; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.ts new file mode 100644 index 0000000..4cec839 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.ts @@ -0,0 +1,68 @@ +import { getContainer } 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 Extension', + description: 'Check file extension', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'extensions', + type: 'string', + defaultValue: 'mkv,mp4', + inputUI: { + type: 'text', + }, + tooltip: 'A comma separated list of extensions to check', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'File is one of extensions', + }, + { + number: 2, + tooltip: 'File is not one of extensions', + }, + ], +}); + +// 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 extensions = String(args.inputs.extensions); + const extensionArray = extensions.trim().split(','); + + const extension = getContainer(args.inputFileObj._id); + + let extensionMatch = false; + + if (extensionArray.includes(extension)) { + extensionMatch = true; + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: extensionMatch ? 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 new file mode 100644 index 0000000..04043c5 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/checkFileSize/1.0.0/index.ts @@ -0,0 +1,100 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Check File Size', + description: 'Check size of working file', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [ + { + name: 'unit', + type: 'string', + defaultValue: 'GB', + inputUI: { + type: 'dropdown', + options: [ + 'B', + 'KB', + 'MB', + 'GB', + ], + }, + 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 greaterThanBytes = Number(args.inputs.greaterThan); + let lessThanBytes = Number(args.inputs.lessThan); + const fileSizeBytes = args.inputFileObj.file_size * 1000 * 1000; + + if (args.inputs.unit === 'KB') { + greaterThanBytes *= 1000; + lessThanBytes *= 1000; + } else if (args.inputs.unit === 'MB') { + greaterThanBytes *= 1000000; + lessThanBytes *= 1000000; + } else if (args.inputs.unit === 'GB') { + greaterThanBytes *= 1000000000; + lessThanBytes *= 1000000000; + } + + if (fileSizeBytes >= greaterThanBytes && fileSizeBytes <= lessThanBytes) { + isWithinRange = true; + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.ts new file mode 100644 index 0000000..b9ab91b --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/compareFileSize/1.0.0/index.ts @@ -0,0 +1,62 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Compare File Size', + description: 'Compare file size of working file compared to original file', + style: { + borderColor: 'orange', + }, + tags: '', + + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Working file is smaller than original file', + }, + { + number: 2, + tooltip: 'Working file is same size as original file', + }, + + { + number: 3, + tooltip: 'Working file is larger than original file', + }, + ], +}); + +// 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 outputNumber = 1; + + if (args.inputFileObj.file_size < args.originalLibraryFile.file_size) { + outputNumber = 1; + } else if (args.inputFileObj.file_size === args.originalLibraryFile.file_size) { + outputNumber = 2; + } else if (args.inputFileObj.file_size > args.originalLibraryFile.file_size) { + outputNumber = 3; + } + + 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 000b7d8..12f9f9e 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/copyToDirectory/1.0.0/index.ts @@ -1,3 +1,5 @@ +import { promises as fs } from 'fs'; +import { getContainer, getFileName } from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, IpluginInputArgs, @@ -5,12 +7,11 @@ import { } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ -const details = ():IpluginDetails => ({ +const details = (): IpluginDetails => ({ name: 'Copy to Directory', description: 'Copy the working file to a directory', style: { borderColor: 'green', - opacity: 0.5, }, tags: '', @@ -19,19 +20,26 @@ const details = ():IpluginDetails => ({ icon: 'faArrowRight', inputs: [ { - name: 'target_codec', + name: 'outputDirectory', type: 'string', - defaultValue: 'hevc', + defaultValue: '', inputUI: { - type: 'dropdown', + type: 'text', + }, + tooltip: 'Specify ouput directory', + }, + { + name: 'makeWorkingFile', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'text', options: [ - 'hevc', - // 'vp9', - 'h264', - // 'vp8', + 'false', + 'true', ], }, - tooltip: 'Specify the codec to use', + tooltip: 'Make the copied file the working file', }, ], outputs: [ @@ -43,13 +51,28 @@ 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 originalFileName = getFileName(args.originalLibraryFile._id); + const newContainer = getContainer(args.inputFileObj._id); + + const outputPath = `${args.inputs.outputDirectory}/${originalFileName}.${newContainer}`; + + await fs.copyFile(args.inputFileObj._id, outputPath); + + let workingFile = args.inputFileObj._id; + + if (args.inputs.makeWorkingFile) { + workingFile = outputPath; + } + return { - outputFileObj: args.inputFileObj, + outputFileObj: { + _id: workingFile, + }, outputNumber: 1, variables: args.variables, }; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.ts index 8fbcbcd..c3348cd 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.ts @@ -10,6 +10,7 @@ const details = ():IpluginDetails => ({ description: 'Move working file to directory.', style: { borderColor: 'green', + opacity: 0.5, }, tags: '', isStartPlugin: false, diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts index 631230a..eae65e6 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts @@ -1,3 +1,5 @@ +import { promises as fs } from 'fs'; +import { getContainer, getFileName } from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, IpluginInputArgs, @@ -10,14 +12,23 @@ const details = ():IpluginDetails => ({ description: 'Move working file to directory.', style: { borderColor: 'green', - opacity: 0.5, }, tags: '', isStartPlugin: false, sidebarPosition: -1, icon: 'faArrowRight', - inputs: [], + inputs: [ + { + name: 'outputDirectory', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Specify ouput directory', + }, + ], outputs: [ { number: 1, @@ -27,13 +38,22 @@ 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 originalFileName = getFileName(args.originalLibraryFile._id); + const newContainer = getContainer(args.inputFileObj._id); + + const outputPath = `${args.inputs.outputDirectory}/${originalFileName}.${newContainer}`; + + await fs.rename(args.inputFileObj._id, outputPath); + return { - outputFileObj: args.inputFileObj, + outputFileObj: { + _id: outputPath, + }, outputNumber: 1, variables: args.variables, }; diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts index 0d9cc1c..a3c478e 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts @@ -7,7 +7,7 @@ import { /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = ():IpluginDetails => ({ name: 'Replace Original File', - description: 'Replace the origial file', + description: 'Replace the original file', style: { borderColor: 'green', }, @@ -15,23 +15,7 @@ const details = ():IpluginDetails => ({ isStartPlugin: false, sidebarPosition: -1, icon: 'faArrowRight', - inputs: [ - { - name: 'target_codec', - type: 'string', - defaultValue: 'hevc', - inputUI: { - type: 'dropdown', - options: [ - 'hevc', - // 'vp9', - 'h264', - // 'vp8', - ], - }, - tooltip: 'Specify the codec to use', - }, - ], + inputs: [], outputs: [ { number: 1, diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.ts new file mode 100644 index 0000000..4ae3b77 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.ts @@ -0,0 +1,44 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = ():IpluginDetails => ({ + name: 'Set Original File', + description: 'Set the working file to the original file', + style: { + borderColor: 'green', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: '', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + return { + outputFileObj: { + _id: args.originalLibraryFile._id, + }, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoScale/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/unpack/1.0.0/index.ts similarity index 84% rename from FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoScale/1.0.0/index.ts rename to FlowPluginsTs/CommunityFlowPlugins/file/unpack/1.0.0/index.ts index 620a636..fbff155 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSetVideoScale/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/unpack/1.0.0/index.ts @@ -5,17 +5,17 @@ import { } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ -const details = () :IpluginDetails => ({ - name: 'Set Video Scale', - description: 'Change video scale', +const details = ():IpluginDetails => ({ + name: 'Unpack File', + description: 'Unpack a file', style: { - borderColor: '#6efefc', + borderColor: 'green', opacity: 0.5, }, - tags: 'video', + tags: '', isStartPlugin: false, sidebarPosition: -1, - icon: '', + icon: 'faArrowRight', inputs: [], outputs: [ { diff --git a/FlowPluginsTs/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.ts new file mode 100644 index 0000000..3550a08 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/handbrake/handbrakeCustomArguments/1.0.0/index.ts @@ -0,0 +1,135 @@ +import { promises as fs } from 'fs'; +import { CLI } from '../../../../FlowHelpers/1.0.0/cliUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = ():IpluginDetails => ({ + name: 'HandBrake Custom Arguments', + description: 'HandBrake Custom Arguments', + style: { + borderColor: 'green', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: '', + inputs: [ + { + name: 'customArguments', + type: 'string', + defaultValue: '-Z "Fast 1080p30" --all-subtitles', + inputUI: { + type: 'text', + }, + tooltip: 'Specify HandBrake arguments', + }, + { + name: 'jsonPreset', + type: 'string', + defaultValue: '', + inputUI: { + type: 'text', + }, + tooltip: 'Paste a HandBrake JSON preset here. Leave blank to disable.', + }, + { + name: 'container', + type: 'string', + defaultValue: 'mkv', + inputUI: { + type: 'dropdown', + options: [ + 'mkv', + 'mp4', + 'm4v', + 'avi', + 'mov', + 'mpg', + 'mpeg', + ], + }, + tooltip: 'Specify HandBrake arguments', + }, + ], + 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 customArguments = String(args.inputs.customArguments); + const container = String(args.inputs.container); + + const outputFilePath = `${args.workDir}/tempFile_${new Date().getTime()}.${container}`; + + const presetString = String(args.inputs.jsonPreset); + + const cliArgs = [ + '-i', + `${args.inputFileObj._id}`, + '-o', + `${outputFilePath}`, + ]; + + const presetPath = `${args.workDir}/preset.json`; + + if (presetString.trim() !== '') { + const preset = JSON.parse(presetString); + await fs.writeFile(presetPath, JSON.stringify(preset, null, 2)); + cliArgs.push('--preset-import-file'); + cliArgs.push(presetPath); + cliArgs.push('-Z'); + cliArgs.push(preset.PresetList[0].PresetName); + } else { + cliArgs.push(...args.deps.parseArgsStringToArgv(customArguments, '', '')); + } + + args.updateWorker({ + CLIType: args.handbrakePath, + preset: cliArgs.join(' '), + }); + + const cli = new CLI({ + cli: args.handbrakePath, + 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 HandBrake failed'); + throw new Error('Running HandBrake failed'); + } + + args.logOutcome('tSuc'); + + return { + outputFileObj: { + _id: outputFilePath, + }, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/input/inputFile/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/input/inputFile/1.0.0/index.ts index 18f4368..299cab9 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/input/inputFile/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/input/inputFile/1.0.0/index.ts @@ -7,7 +7,7 @@ import { /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ const details = ():IpluginDetails => ({ name: 'Input File', - description: 'Transcode a video file using ffmpeg. GPU transcoding will be used if possible.', + description: 'Start the flow with an input file', style: { borderColor: 'pink', }, diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/failFlow/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/failFlow/1.0.0/index.ts new file mode 100644 index 0000000..7cccee8 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/failFlow/1.0.0/index.ts @@ -0,0 +1,39 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = ():IpluginDetails => ({ + name: 'Fail Flow', + description: 'Force the flow to fail and be move to the error table', + style: { + borderColor: 'red', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faExclamationTriangle', + inputs: [], + outputs: [], +}); + +// 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); + + throw new Error('Forcing flow to fail!'); + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.ts new file mode 100644 index 0000000..235ab75 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.ts @@ -0,0 +1,38 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = ():IpluginDetails => ({ + name: 'Go To Flow', + description: 'Go to a different flow', + style: { + borderColor: 'red', + opacity: 0.5, + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faArrowRight', + inputs: [], + outputs: [], +}); + +// 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/runMkvPropEdit/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.ts new file mode 100644 index 0000000..1b39283 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.ts @@ -0,0 +1,67 @@ +import { CLI } from '../../../../FlowHelpers/1.0.0/cliUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = ():IpluginDetails => ({ + name: 'Run MKVPropEdit', + description: 'Run MKVPropEdit on a file to update metadata which' + + ' FFmpeg doesn\'t typically update such as stream bitrate.', + style: { + borderColor: 'green', + }, + tags: '', + isStartPlugin: false, + sidebarPosition: -1, + icon: '', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = 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 cliArgs = [ + '--add-track-statistics-tags', + args.inputFileObj._id, + ]; + + const cli = new CLI({ + cli: args.mkvpropeditPath, + 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 MKVPropEdit failed'); + throw new Error('Running MKVPropEdit failed'); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/tools/unpack/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/tools/unpack/1.0.0/index.ts deleted file mode 100644 index 64ff22f..0000000 --- a/FlowPluginsTs/CommunityFlowPlugins/tools/unpack/1.0.0/index.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { - IpluginDetails, - IpluginInputArgs, - IpluginOutputArgs, -} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; - -/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ -const details = ():IpluginDetails => ({ - name: 'Unpack File', - description: 'Unpack a file', - style: { - borderColor: 'green', - opacity: 0.5, - }, - tags: '', - isStartPlugin: false, - sidebarPosition: -1, - icon: 'faArrowRight', - inputs: [ - { - name: 'target_codec', - type: 'string', - defaultValue: 'hevc', - inputUI: { - type: 'dropdown', - options: [ - 'hevc', - // 'vp9', - 'h264', - // 'vp8', - ], - }, - tooltip: 'Specify the codec to use', - }, - ], - outputs: [ - { - number: 1, - tooltip: 'Continue to next plugin', - }, - ], -}); - -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'); - 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 currentPath = args.inputFileObj._id; - const newPath = getNewPath(args.originalLibraryFile._id, currentPath); - const newPathTmp = `${newPath}.tmp`; - - args.jobLog(JSON.stringify({ - currentPath, - newPath, - newPathTmp, - })); - - await new Promise((resolve) => setTimeout(resolve, 2000)); - - fs.renameSync(currentPath, newPathTmp); - - if (fs.existsSync(newPath)) { - fs.unlinkSync(newPath); - } - - await new Promise((resolve) => setTimeout(resolve, 2000)); - fs.renameSync(newPathTmp, newPath); - - return { - outputFileObj: { - _id: newPath, - }, - 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 8b90ce6..4026f12 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/tools/webRequest/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/tools/webRequest/1.0.0/index.ts @@ -16,23 +16,7 @@ const details = ():IpluginDetails => ({ isStartPlugin: false, sidebarPosition: -1, icon: 'faArrowRight', - inputs: [ - { - name: 'target_codec', - type: 'string', - defaultValue: 'hevc', - inputUI: { - type: 'dropdown', - options: [ - 'hevc', - // 'vp9', - 'h264', - // 'vp8', - ], - }, - tooltip: 'Specify the codec to use', - }, - ], + inputs: [], outputs: [ { number: 1, @@ -41,49 +25,14 @@ 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'); +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 currentPath = args.inputFileObj._id; - const newPath = getNewPath(args.originalLibraryFile._id, currentPath); - const newPathTmp = `${newPath}.tmp`; - - args.jobLog(JSON.stringify({ - currentPath, - newPath, - newPathTmp, - })); - - await new Promise((resolve) => setTimeout(resolve, 2000)); - - fs.renameSync(currentPath, newPathTmp); - - if (fs.existsSync(newPath)) { - fs.unlinkSync(newPath); - } - - await new Promise((resolve) => setTimeout(resolve, 2000)); - fs.renameSync(newPathTmp, newPath); - return { - outputFileObj: { - _id: newPath, - }, + outputFileObj: args.inputFileObj, outputNumber: 1, variables: args.variables, }; diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/check10Bit/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/check10Bit/1.0.0/index.ts new file mode 100644 index 0000000..88ac7e9 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/video/check10Bit/1.0.0/index.ts @@ -0,0 +1,55 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = (): IpluginDetails => ({ + name: 'Check 10 Bit Video', + description: 'Check if a file is 10 bit video', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + sidebarPosition: -1, + icon: 'faQuestion', + inputs: [], + outputs: [ + { + number: 1, + tooltip: 'File is 10 bit video', + }, + { + number: 2, + tooltip: 'File is not 10 bit video', + }, + ], +}); + +// 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 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; + } + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: is10Bit ? 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 new file mode 100644 index 0000000..45c75d2 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoBitrate/1.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: 'Check Video Bitrate', + description: 'Check if video bitrate is within a specific range', + style: { + borderColor: 'orange', + }, + tags: 'video', + isStartPlugin: false, + 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; + } + + if (args.inputFileObj?.mediaInfo?.track) { + args.inputFileObj.mediaInfo.track.forEach((stream) => { + if (stream['@type'] === 'video') { + if (stream.BitRate >= greaterThanBits && stream.BitRate <= lessThanBits) { + isWithinRange = true; + } + } + }); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: isWithinRange ? 1 : 2, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.ts index d9c8bac..946ecf8 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/video/checkVideoCodec/1.0.0/index.ts @@ -52,12 +52,13 @@ const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { let hasCodec = false; - // @ts-expect-error type - args.inputFileObj.ffProbeData.streams.forEach((stream) => { - if (stream.codec_type === 'video' && stream.codec_name === args.inputs.codec) { - hasCodec = true; - } - }); + if (args.inputFileObj.ffProbeData.streams) { + args.inputFileObj.ffProbeData.streams.forEach((stream) => { + if (stream.codec_type === 'video' && stream.codec_name === args.inputs.codec) { + hasCodec = true; + } + }); + } return { outputFileObj: args.inputFileObj, diff --git a/FlowPluginsTs/CommunityFlowTemplates/video/basicVideo.ts b/FlowPluginsTs/CommunityFlowTemplates/video/basicVideo.ts index 10650dd..1acb5a4 100644 --- a/FlowPluginsTs/CommunityFlowTemplates/video/basicVideo.ts +++ b/FlowPluginsTs/CommunityFlowTemplates/video/basicVideo.ts @@ -1,197 +1,129 @@ const details = () => ({ - name: 'Basic Video Template', - description: 'Basic Video Template', + name: 'Basic HEVC Video Flow', + description: 'Flow description', tags: 'video', flowPlugins: [ { - id: 'nr55HwObs', - version: '1.0.0', - pluginName: 'inputFile', - inputsDB: {}, - position: { - x: 371.99540048613267, - y: -463.08388975391864, - }, - data: { - label: 'Input File', - }, + name: 'Input File', sourceRepo: 'Community', - }, - { - id: 'aDDcNO50Q', + pluginName: 'inputFile', version: '1.0.0', - pluginName: 'checkFileMedium', - inputsDB: {}, + id: 'pE6rU7gkW', position: { - x: 529.392455443893, - y: -349.448086326927, + x: 758.5809635618224, + y: 117.19206188888086, }, - data: { - label: 'Check File Medium', - }, - sourceRepo: 'Community', }, { - id: 'dBwjiWjfA', + name: 'Check if hevc', + sourceRepo: 'Community', + pluginName: 'checkVideoCodec', version: '1.0.0', - pluginName: 'replaceOriginalFile', - inputsDB: {}, + id: '91b7IrsEc', position: { - x: 517.8566785616271, - y: 145.89446592709146, - }, - data: { - label: 'Replace Original File', + x: 672.4549563302081, + y: 253.11148102973914, }, - sourceRepo: 'Community', }, { - id: '1m231hS5K', + name: 'Start', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandStart', version: '1.0.0', - pluginName: 'ffmpegCommandSetVideoEncoder', - inputsDB: {}, + id: '4Swd6qzvc', position: { - x: 104.71582826971436, - y: -44.833187887976514, - }, - data: { - label: 'Set Video Encoder', + x: 499.4549563302081, + y: 367.1114810297392, }, - sourceRepo: 'Community', }, { - id: 'iYEwAG4rk', + name: 'Execute', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandExecute', version: '1.0.0', - pluginName: 'ffmpegCommandStart', - inputsDB: {}, + id: '450g167D8', position: { - x: 138.43458422405857, - y: -149.36438875566702, - }, - data: { - label: 'Start', + x: 496.4549563302081, + y: 653.1114810297393, }, - sourceRepo: 'Community', }, { - id: 'staFGJ7lQ', + name: 'Set Video Encoder', + sourceRepo: 'Community', + pluginName: 'ffmpegCommandSetVideoEncoder', version: '1.0.0', - pluginName: 'ffmpegCommandExecute', - inputsDB: {}, + id: '8B_6pRd_U', position: { - x: 104.4813203353468, - y: 63.43024405079595, - }, - data: { - label: 'Execute', + x: 498.4549563302081, + y: 527.1114810297393, }, - sourceRepo: 'Community', }, { - id: '3sQBco3U6', + name: 'Replace Original File', + sourceRepo: 'Community', + pluginName: 'replaceOriginalFile', version: '1.0.0', - pluginName: 'checkVideoCodec', - inputsDB: {}, + id: '4fkfOyR3l', position: { - x: 252.92777615144564, - y: -274.3710285987198, + x: 820.4549563302082, + y: 742.2114810297393, }, - data: { - label: 'Check if HEVC', - }, - sourceRepo: 'Community', }, ], flowEdges: [ { - source: 'nr55HwObs', + source: 'pE6rU7gkW', sourceHandle: '1', - target: 'aDDcNO50Q', + target: '91b7IrsEc', targetHandle: null, + id: 'HhF4rw2DZ', animated: true, type: 'smoothstep', - id: 'reactflow__edge-nr55HwObs1-aDDcNO50Q', }, { - source: 'aDDcNO50Q', - sourceHandle: '3', - target: 'dBwjiWjfA', - targetHandle: null, - animated: true, - type: 'smoothstep', - id: 'reactflow__edge-aDDcNO50Q3-dBwjiWjfA', - }, - { - source: 'aDDcNO50Q', + source: '91b7IrsEc', sourceHandle: '2', - target: 'dBwjiWjfA', + target: '4Swd6qzvc', targetHandle: null, + id: 'jJizyFUcr', animated: true, type: 'smoothstep', - id: 'reactflow__edge-aDDcNO50Q2-dBwjiWjfA', }, { - source: 'iYEwAG4rk', + source: '4Swd6qzvc', sourceHandle: '1', - target: '1m231hS5K', + target: '8B_6pRd_U', targetHandle: null, + id: '3Df7Xoy93', animated: true, type: 'smoothstep', - id: 'reactflow__edge-iYEwAG4rk1-1m231hS5K', }, { - source: '1m231hS5K', + source: '8B_6pRd_U', sourceHandle: '1', - target: 'staFGJ7lQ', + target: '450g167D8', targetHandle: null, + id: 'BQerEKase', animated: true, type: 'smoothstep', - id: 'reactflow__edge-1m231hS5K1-staFGJ7lQ', }, { - source: 'staFGJ7lQ', - sourceHandle: '2', - target: 'dBwjiWjfA', - targetHandle: null, - animated: true, - type: 'smoothstep', - id: 'reactflow__edge-staFGJ7lQ2-dBwjiWjfA', - }, - { - source: 'staFGJ7lQ', - sourceHandle: '1', - target: 'dBwjiWjfA', - targetHandle: null, - animated: true, - type: 'smoothstep', - id: 'reactflow__edge-staFGJ7lQ1-dBwjiWjfA', - }, - { - source: 'aDDcNO50Q', + source: '450g167D8', sourceHandle: '1', - target: '3sQBco3U6', + target: '4fkfOyR3l', targetHandle: null, + id: 'rE5Dsh9KM', animated: true, type: 'smoothstep', - id: 'reactflow__edge-aDDcNO50Q1-3sQBco3U6', }, { - source: '3sQBco3U6', + source: '91b7IrsEc', sourceHandle: '1', - target: 'dBwjiWjfA', - targetHandle: null, - animated: true, - type: 'smoothstep', - id: 'reactflow__edge-3sQBco3U61-dBwjiWjfA', - }, - { - source: '3sQBco3U6', - sourceHandle: '2', - target: 'iYEwAG4rk', + target: '4fkfOyR3l', targetHandle: null, + id: 'W2nVG7ts5', animated: true, type: 'smoothstep', - id: 'reactflow__edge-3sQBco3U62-iYEwAG4rk', }, ], }); diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/utils.ts b/FlowPluginsTs/FlowHelpers/1.0.0/cliUtils.ts similarity index 95% rename from FlowPluginsTs/FlowHelpers/1.0.0/utils.ts rename to FlowPluginsTs/FlowHelpers/1.0.0/cliUtils.ts index 63dab96..30025ff 100644 --- a/FlowPluginsTs/FlowHelpers/1.0.0/utils.ts +++ b/FlowPluginsTs/FlowHelpers/1.0.0/cliUtils.ts @@ -240,7 +240,7 @@ class CLI { const errorLogFull: string[] = []; // eslint-disable-next-line no-console - console.log(`Running ${this.config.cli} ${this.config.spawnArgs.join(' ')}`); + this.config.jobLog(`Running ${this.config.cli} ${this.config.spawnArgs.join(' ')}`); const cliExitCode: number = await new Promise((resolve) => { try { const opts = this.config.spawnOpts || {}; @@ -248,14 +248,14 @@ class CLI { thread.stdout.on('data', (data: string) => { // eslint-disable-next-line no-console - console.log(data.toString()); + // console.log(data.toString()); errorLogFull.push(data.toString()); this.parseOutput(data); }); thread.stderr.on('data', (data: string) => { // eslint-disable-next-line no-console - console.log(data.toString()); + // console.log(data.toString()); errorLogFull.push(data.toString()); this.parseOutput(data); }); @@ -272,7 +272,7 @@ class CLI { thread.on('close', (code: number) => { if (code !== 0) { // eslint-disable-next-line no-console - console.log(code, 'FFmpeg error'); + console.log(code, 'CLI error'); } resolve(code); }); @@ -284,6 +284,10 @@ class CLI { } }); + if (!this.config.logFullCliOutput) { + this.config.jobLog(errorLogFull.slice(-1000).join('')); + } + return { cliExitCode, errorLogFull, diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts b/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts new file mode 100644 index 0000000..08e122e --- /dev/null +++ b/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts @@ -0,0 +1,13 @@ +export const getContainer = (filePath: string):string => { + const parts = filePath.split('.'); + return parts[parts.length - 1]; +}; + +export const getFileName = (filePath: string):string => { + const parts = filePath.split('/'); + const fileNameAndContainer = parts[parts.length - 1]; + const parts2 = fileNameAndContainer.split('.'); + return parts2[0]; +}; + +export const getFfType = (codecType:string):string => (codecType === 'video' ? 'v' : 'a'); diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.test.ts b/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.test.ts new file mode 100644 index 0000000..f1cfa1c --- /dev/null +++ b/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.test.ts @@ -0,0 +1,24 @@ +import { getEncoder } from './hardwareUtils'; + +const run = async () => { + const encoderProperties = await getEncoder({ + targetCodec: 'h264', + hardwareEncoding: true, + // @ts-expect-error type + args: { + workerType: 'transcodegpu', + ffmpegPath: 'ffmpeg', + jobLog: (t:string) => { + // eslint-disable-next-line no-console + console.log(t); + }, + }, + }); + + // eslint-disable-next-line no-console + console.log({ + encoderProperties, + }); +}; + +void run(); diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.ts b/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.ts new file mode 100644 index 0000000..47cc3b1 --- /dev/null +++ b/FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.ts @@ -0,0 +1,293 @@ +import { IpluginInputArgs } from './interfaces/interfaces'; + +export const hasEncoder = async ({ + ffmpegPath, + encoder, + inputArgs, + filter, +}: { + ffmpegPath: string, + encoder: string, + inputArgs: string[], + filter: string, +}): Promise => { + const { exec } = require('child_process'); + let isEnabled = false; + try { + isEnabled = await new Promise((resolve) => { + 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`; + exec(command, ( + // eslint-disable-next-line + error: any, + // stdout, + // stderr, + ) => { + if (error) { + resolve(false); + return; + } + resolve(true); + }); + }); + } catch (err) { + // eslint-disable-next-line no-console + console.log(err); + } + + return isEnabled; +}; + +interface IgpuEncoder { + encoder: string, + enabled: boolean, + + inputArgs: string[], + outputArgs: string[], + filter: string, +} + +// credit to UNCode101 for this +export const getBestNvencDevice = ({ + args, + nvencDevice, +}: { + args: IpluginInputArgs + nvencDevice: IgpuEncoder, +}): IgpuEncoder => { + const { execSync } = require('child_process'); + let gpu_num = -1; + let lowest_gpu_util = 100000; + let result_util = 0; + let gpu_count = -1; + let gpu_names = ''; + const gpus_to_exclude: string[] = []; + // inputs.exclude_gpus === '' ? [] : inputs.exclude_gpus.split(',').map(Number); + try { + gpu_names = execSync('nvidia-smi --query-gpu=name --format=csv,noheader'); + gpu_names = gpu_names.toString().trim(); + const gpu_namesArr = gpu_names.split(/\r?\n/); + /* When nvidia-smi returns an error it contains 'nvidia-smi' in the error + Example: Linux: nvidia-smi: command not found + Windows: 'nvidia-smi' is not recognized as an internal or external command, + operable program or batch file. */ + if (!gpu_namesArr[0].includes('nvidia-smi')) { + gpu_count = gpu_namesArr.length; + } + } catch (error) { + args.jobLog('Error in reading nvidia-smi output! \n'); + } + + if (gpu_count > 0) { + for (let gpui = 0; gpui < gpu_count; gpui += 1) { + // Check if GPU # is in GPUs to exclude + if (gpus_to_exclude.includes(String(gpui))) { + args.jobLog(`GPU ${gpui}: ${gpu_names[gpui]} is in exclusion list, will not be used!\n`); + } else { + try { + const cmd_gpu = `nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits -i ${gpui}`; + result_util = parseInt(execSync(cmd_gpu), 10); + if (!Number.isNaN(result_util)) { // != "No devices were found") { + args.jobLog(`GPU ${gpui} : Utilization ${result_util}%\n`); + + if (result_util < lowest_gpu_util) { + gpu_num = gpui; + lowest_gpu_util = result_util; + } + } + } catch (error) { + args.jobLog(`Error in reading GPU ${gpui} Utilization\nError: ${error}\n`); + } + } + } + } + if (gpu_num >= 0) { + // eslint-disable-next-line no-param-reassign + nvencDevice.inputArgs.push('-hwaccel_device', `${gpu_num}`); + // eslint-disable-next-line no-param-reassign + nvencDevice.outputArgs.push('-gpu', `${gpu_num}`); + } + + return nvencDevice; +}; + +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; + } + + return false; +}; + +export const getEncoder = async ({ + targetCodec, + hardwareEncoding, + args, +}: { + targetCodec: string, + hardwareEncoding: boolean, + args: IpluginInputArgs, +}): Promise<{ + encoder: string, + inputArgs: string[], + outputArgs: string[], + isGpu: boolean, +}> => { + if ( + args.workerType + && args.workerType.includes('gpu') + && hardwareEncoding && (targetCodec === 'hevc' || targetCodec === 'h264')) { + const gpuEncoders: IgpuEncoder[] = [ + { + encoder: 'hevc_nvenc', + enabled: false, + inputArgs: [ + '-hwaccel', + 'cuda', + ], + outputArgs: [], + filter: '', + }, + { + encoder: 'hevc_amf', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'hevc_vaapi', + inputArgs: [ + '-hwaccel', + 'vaapi', + '-hwaccel_device', + '/dev/dri/renderD128', + '-hwaccel_output_format', + 'vaapi', + ], + outputArgs: [], + enabled: false, + filter: '-vf format=nv12,hwupload', + }, + { + encoder: 'hevc_qsv', + enabled: false, + inputArgs: [ + '-hwaccel', + 'qsv', + ], + outputArgs: [], + filter: '', + }, + { + encoder: 'hevc_videotoolbox', + enabled: false, + inputArgs: [ + '-hwaccel', + 'videotoolbox', + ], + outputArgs: [], + filter: '', + }, + + { + encoder: 'h264_nvenc', + enabled: false, + inputArgs: [ + '-hwaccel', + 'cuda', + ], + outputArgs: [], + filter: '', + }, + { + encoder: 'h264_amf', + enabled: false, + inputArgs: [], + outputArgs: [], + filter: '', + }, + { + encoder: 'h264_qsv', + enabled: false, + inputArgs: [ + '-hwaccel', + 'qsv', + ], + outputArgs: [], + filter: '', + }, + { + encoder: 'h264_videotoolbox', + enabled: false, + inputArgs: [ + '-hwaccel', + 'videotoolbox', + ], + outputArgs: [], + filter: '', + }, + ]; + + const filteredGpuEncoders = gpuEncoders.filter((device) => encoderFilter(device.encoder, targetCodec)); + + // eslint-disable-next-line no-restricted-syntax + for (const gpuEncoder of filteredGpuEncoders) { + // eslint-disable-next-line no-await-in-loop + gpuEncoder.enabled = await hasEncoder({ + ffmpegPath: args.ffmpegPath, + encoder: gpuEncoder.encoder, + inputArgs: gpuEncoder.inputArgs, + filter: gpuEncoder.filter, + }); + } + + const enabledDevices = gpuEncoders.filter((device) => device.enabled === true); + + if (enabledDevices.length > 0) { + if (enabledDevices[0].encoder.includes('nvenc')) { + const res = getBestNvencDevice({ + args, + nvencDevice: enabledDevices[0], + }); + + return { + ...res, + isGpu: true, + }; + } + return { + encoder: enabledDevices[0].encoder, + inputArgs: enabledDevices[0].inputArgs, + outputArgs: enabledDevices[0].outputArgs, + isGpu: true, + }; + } + } + + if (targetCodec === 'hevc') { + return { + encoder: 'libx265', + inputArgs: [], + outputArgs: [], + isGpu: false, + }; + } if (targetCodec === 'h264') { + return { + encoder: 'libx264', + inputArgs: [], + outputArgs: [], + isGpu: false, + }; + } + + return { + encoder: targetCodec, + inputArgs: [], + outputArgs: [], + isGpu: false, + }; +}; diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts b/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts index 71d6233..86c649a 100644 --- a/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts +++ b/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts @@ -2,8 +2,16 @@ import { IFileObject, Istreams } from './synced/IFileObject'; import Ijob from './synced/jobInterface'; export interface IpluginInputUi { - type: 'dropdown' | 'text', - options: string[], + type: 'dropdown' | 'text' | 'textarea', + options?: string[], + style?:Record, + onSelect?: { + 'hevc': { + update: { + quality: '28', + }, + } + }, } export interface IpluginInputs { @@ -43,18 +51,23 @@ export interface IupdateWorker { export interface IffmpegCommandStream extends Istreams { removed: boolean, - targetCodec: string, - args: string[], + forceEncoding: boolean, + inputArgs: string[], + outputArgs: string[], } export interface IffmpegCommand { inputFiles: string[], streams: IffmpegCommandStream[] container: string, + hardwareDecoding: boolean, + shouldProcess: boolean, + overallInputArguments: string[], + overallOuputArguments: string[], } export interface Ivariables { - ffmpegCommand?: IffmpegCommand + ffmpegCommand: IffmpegCommand } export interface IpluginOutputArgs { @@ -63,7 +76,6 @@ export interface IpluginOutputArgs { _id: string, }, variables: Ivariables - } export interface IpluginInputArgs { @@ -92,5 +104,16 @@ export interface IpluginInputArgs { lastSuccessfulRun: any, updateWorker: IupdateWorker, logFullCliOutput: boolean, - logOutcome:(outcome:string) => void, + logOutcome: (outcome: string) => void, + deps: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fsextra: any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + parseArgsStringToArgv: any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + importFresh(path: string): any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + axiosMiddleware: (endpoint: string, data: Record) => Promise, + requireFromString: (pluginText: string, relativePath:string) => Record, + }, } diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/synced/IFileObject.ts b/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/synced/IFileObject.ts index 45ea961..6c36a2c 100644 --- a/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/synced/IFileObject.ts +++ b/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/synced/IFileObject.ts @@ -14,7 +14,7 @@ export interface Itags { } export interface Istreams { codec_name: string; - codec_type?: string, + codec_type: string, bit_rate?: number, channels?: number, tags?: Itags, @@ -134,6 +134,7 @@ export interface ImediaInfo { 'IsStreamable': string, 'Encoded_Application': string, 'Encoded_Library': string, + BitRate: number, 'extra': { 'ErrorDetectionType': string, }