mirror of
https://github.com/gabehf/Tdarr_Plugins.git
synced 2026-03-13 01:10:27 -07:00
Merge branch 'master' into pr/694
This commit is contained in:
commit
7321a63b4a
56 changed files with 1543 additions and 152 deletions
|
|
@ -124,7 +124,7 @@ const plugin = (args: IpluginInputArgs): IpluginOutputArgs => {
|
|||
'mpeg',
|
||||
].includes(container)
|
||||
) {
|
||||
args.variables.ffmpegCommand.overallOuputArguments.push('-fflags', '+genpts');
|
||||
args.variables.ffmpegCommand.overallInputArguments.push('-fflags', '+genpts');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ const details = (): IpluginDetails => ({
|
|||
options: [
|
||||
'auto',
|
||||
'nvenc',
|
||||
'rkmpp',
|
||||
'qsv',
|
||||
'vaapi',
|
||||
'videotoolbox',
|
||||
|
|
|
|||
|
|
@ -10,7 +10,13 @@ import { getFileAbosluteDir } from '../../../../FlowHelpers/1.0.0/fileUtils';
|
|||
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
|
||||
const details = (): IpluginDetails => ({
|
||||
name: 'Delete File',
|
||||
description: 'Delete the working file or original file.',
|
||||
description: `
|
||||
Delete the working file or original file.
|
||||
You don't need to use this plugin to clean up files in the cache, Tdarr will do this automatically after the flow.
|
||||
To manually clear the cache, use the 'Clear Cache' flow plugin.
|
||||
If the working file is deleted, the plugin after this one should load a valid working file,
|
||||
such as 'Set Original File' to load the original file, else subsequent plugins will error.
|
||||
`,
|
||||
style: {
|
||||
borderColor: 'red',
|
||||
},
|
||||
|
|
@ -35,6 +41,32 @@ const details = (): IpluginDetails => ({
|
|||
},
|
||||
tooltip: 'Specify the file to delete',
|
||||
},
|
||||
{
|
||||
label: 'Delete Working File If It\'s The Original File',
|
||||
name: 'deleteWorkingFileIfOriginal',
|
||||
type: 'boolean',
|
||||
defaultValue: 'true',
|
||||
inputUI: {
|
||||
type: 'switch',
|
||||
displayConditions: {
|
||||
logic: 'AND',
|
||||
sets: [
|
||||
{
|
||||
logic: 'AND',
|
||||
inputs: [
|
||||
{
|
||||
name: 'fileToDelete',
|
||||
value: 'workingFile',
|
||||
condition: '===',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
tooltip: 'If the option above is set to delete the working file,'
|
||||
+ ' and the working file is the original file, delete the file.',
|
||||
},
|
||||
{
|
||||
label: 'Delete Parent Folder If Empty',
|
||||
name: 'deleteParentFolderIfEmpty',
|
||||
|
|
@ -61,11 +93,29 @@ const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
|
|||
args.inputs = lib.loadDefaultValues(args.inputs, details);
|
||||
|
||||
const fileToDelete = String(args.inputs.fileToDelete);
|
||||
const { deleteParentFolderIfEmpty } = args.inputs;
|
||||
const {
|
||||
deleteParentFolderIfEmpty,
|
||||
deleteWorkingFileIfOriginal,
|
||||
} = args.inputs;
|
||||
|
||||
if (fileToDelete === 'workingFile') {
|
||||
args.jobLog(`Deleting working file ${args.inputFileObj._id}`);
|
||||
await fsp.unlink(args.inputFileObj._id);
|
||||
const workingFileIsOriginal = args.originalLibraryFile._id === args.inputFileObj._id;
|
||||
|
||||
if (workingFileIsOriginal) {
|
||||
args.jobLog('Working file is the original file!');
|
||||
} else {
|
||||
args.jobLog('Working file is not the original file!');
|
||||
}
|
||||
|
||||
if (
|
||||
(workingFileIsOriginal && deleteWorkingFileIfOriginal)
|
||||
|| !workingFileIsOriginal
|
||||
) {
|
||||
args.jobLog(`Deleting working file ${args.inputFileObj._id}`);
|
||||
await fsp.unlink(args.inputFileObj._id);
|
||||
} else {
|
||||
args.jobLog('Skipping delete of working file because it is the original file');
|
||||
}
|
||||
} else if (fileToDelete === 'originalFile') {
|
||||
args.jobLog(`Deleting original file ${args.originalLibraryFile._id}`);
|
||||
await fsp.unlink(args.originalLibraryFile._id);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ import {
|
|||
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
|
||||
const details = (): IpluginDetails => ({
|
||||
name: 'Replace Original File',
|
||||
description: 'Replace the original file. If the file hasn\'t changed then no action is taken.',
|
||||
description: `
|
||||
Replace the original file with the 'working' file passed into this plugin.
|
||||
If the file hasn't changed then no action is taken.
|
||||
Note: The 'working' filename and container will replace the original filename and container.
|
||||
`,
|
||||
style: {
|
||||
borderColor: 'green',
|
||||
},
|
||||
|
|
@ -75,10 +79,18 @@ const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
|
|||
args,
|
||||
});
|
||||
|
||||
const originalFileExists = await fileExists(args.originalLibraryFile._id);
|
||||
const currentFileIsNotOriginal = args.originalLibraryFile._id !== currentPath;
|
||||
|
||||
args.jobLog(JSON.stringify({
|
||||
originalFileExists,
|
||||
currentFileIsNotOriginal,
|
||||
}));
|
||||
|
||||
// delete original file
|
||||
if (
|
||||
await fileExists(args.originalLibraryFile._id)
|
||||
&& args.originalLibraryFile._id !== currentPath
|
||||
originalFileExists
|
||||
&& currentFileIsNotOriginal
|
||||
) {
|
||||
args.jobLog(`Deleting original file:${args.originalLibraryFile._id}`);
|
||||
await fsp.unlink(args.originalLibraryFile._id);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import {
|
|||
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
|
||||
const details = ():IpluginDetails => ({
|
||||
name: 'Set Original File',
|
||||
description: 'Set the working file to the original file path at the very start of the flow. '
|
||||
+ 'The original file won\'t be affected by subsequent plugins (E.g. Replace Original File).',
|
||||
description: 'Set the working file to the original file path at the very start of the flow.',
|
||||
style: {
|
||||
borderColor: 'green',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ const details = (): IpluginDetails => ({
|
|||
options: [
|
||||
'hevc_nvenc',
|
||||
'hevc_amf',
|
||||
'hevc_rkmpp',
|
||||
'hevc_vaapi',
|
||||
'hevc_qsv',
|
||||
'hevc_videotoolbox',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
import { promises as fsp } from 'fs';
|
||||
import {
|
||||
IpluginDetails,
|
||||
IpluginInputArgs,
|
||||
IpluginOutputArgs,
|
||||
} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces';
|
||||
import normJoinPath from '../../../../FlowHelpers/1.0.0/normJoinPath';
|
||||
|
||||
const helperText = `
|
||||
jobCache: Clears all other files in this job's cache folder (which is a subfolder of the library cache).
|
||||
libraryCache: Clears all other files in the library cache.
|
||||
`;
|
||||
|
||||
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
|
||||
const details = (): IpluginDetails => ({
|
||||
name: 'Clear Cache',
|
||||
description: `
|
||||
This plugin allows you to clear various cache folders, keeping only the current 'working' file.
|
||||
${helperText}
|
||||
|
||||
`,
|
||||
style: {
|
||||
borderColor: 'red',
|
||||
},
|
||||
tags: '',
|
||||
isStartPlugin: false,
|
||||
pType: '',
|
||||
requiresVersion: '2.11.01',
|
||||
sidebarPosition: -1,
|
||||
icon: 'faTrash',
|
||||
inputs: [
|
||||
{
|
||||
label: 'Cache To Clear',
|
||||
name: 'cacheToClear',
|
||||
type: 'string',
|
||||
defaultValue: 'jobCache',
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'jobCache',
|
||||
'libraryCache',
|
||||
],
|
||||
},
|
||||
tooltip: `Specify which cache to clear
|
||||
${helperText}
|
||||
`,
|
||||
},
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
number: 1,
|
||||
tooltip: 'Continue to next plugin',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
|
||||
const lib = require('../../../../../methods/lib')();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
|
||||
args.inputs = lib.loadDefaultValues(args.inputs, details);
|
||||
|
||||
const { cacheToClear } = args.inputs;
|
||||
|
||||
const currentFile = args.inputFileObj._id;
|
||||
const jobCacheDir = args.workDir;
|
||||
const libraryCacheDir = args.librarySettings.cache;
|
||||
|
||||
let folderToClear = '';
|
||||
|
||||
if (cacheToClear === 'jobCache') {
|
||||
folderToClear = jobCacheDir;
|
||||
} else if (cacheToClear === 'libraryCache') {
|
||||
folderToClear = libraryCacheDir;
|
||||
}
|
||||
|
||||
args.jobLog(`Clearing ${cacheToClear} folder: "${folderToClear}"`);
|
||||
args.jobLog(`Keeping current file: "${currentFile}"`);
|
||||
|
||||
const traverseFolder = async (dir:string) => {
|
||||
const filesInDir = (await fsp.readdir(dir)).map((file) => normJoinPath({
|
||||
upath: args.deps.upath,
|
||||
paths: [
|
||||
dir,
|
||||
file,
|
||||
],
|
||||
}));
|
||||
|
||||
for (let i = 0; i < filesInDir.length; i += 1) {
|
||||
const file = filesInDir[i];
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const stat = await fsp.stat(file);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await traverseFolder(file);
|
||||
} else if (
|
||||
file !== currentFile
|
||||
// prevent deleting non Tdarr cache files
|
||||
&& file.includes('tdarr-workDir2')
|
||||
) {
|
||||
args.jobLog(`Deleting "${file}"`);
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await fsp.unlink(file);
|
||||
} catch (err) {
|
||||
args.jobLog(`File delete error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await traverseFolder(folderToClear);
|
||||
|
||||
return {
|
||||
outputFileObj: args.inputFileObj,
|
||||
outputNumber: 1,
|
||||
variables: args.variables,
|
||||
};
|
||||
};
|
||||
export {
|
||||
details,
|
||||
plugin,
|
||||
};
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/* eslint-disable max-len */
|
||||
|
||||
import { promises as fsp } from 'fs';
|
||||
import { getPluginWorkDir } 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: 'Custom JS Function',
|
||||
description: 'Write a custom function in JS to run with up to 4 outputs',
|
||||
style: {
|
||||
borderColor: 'green',
|
||||
},
|
||||
tags: '',
|
||||
isStartPlugin: false,
|
||||
pType: '',
|
||||
requiresVersion: '2.11.01',
|
||||
sidebarPosition: -1,
|
||||
icon: 'faArrowRight',
|
||||
inputs: [
|
||||
{
|
||||
label: 'JS Code',
|
||||
name: 'code',
|
||||
type: 'string',
|
||||
defaultValue: `
|
||||
module.exports = async (args) => {
|
||||
|
||||
// see args object data here https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts
|
||||
// example setting flow variable: https://github.com/HaveAGitGat/Tdarr/issues/1147#issuecomment-2593348443
|
||||
// example reading ffmpeg metadata: https://github.com/HaveAGitGat/Tdarr_Plugins/issues/737#issuecomment-2581536112
|
||||
// example setting working file as previous working file: https://github.com/HaveAGitGat/Tdarr/issues/1106#issuecomment-2622177459
|
||||
|
||||
// some example file data:
|
||||
console.log(args.inputFileObj._id)
|
||||
console.log(args.inputFileObj.file_size)
|
||||
console.log(args.inputFileObj.ffProbeData.streams[0].codec_name)
|
||||
console.log(args.inputFileObj.mediaInfo.track[0].BitRate)
|
||||
|
||||
// access global variable:
|
||||
console.log(args.userVariables.global.test)
|
||||
// access library variable:
|
||||
console.log(args.userVariables.library.test)
|
||||
|
||||
|
||||
|
||||
// do something here
|
||||
|
||||
return {
|
||||
outputFileObj: args.inputFileObj,
|
||||
outputNumber: 1,
|
||||
variables: args.variables,
|
||||
};
|
||||
}
|
||||
`,
|
||||
inputUI: {
|
||||
type: 'textarea',
|
||||
style: {
|
||||
height: '200px',
|
||||
},
|
||||
},
|
||||
tooltip: 'Write your custom function here',
|
||||
},
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
number: 1,
|
||||
tooltip: 'Continue to output 1',
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
tooltip: 'Continue to output 2',
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
tooltip: 'Continue to output 3',
|
||||
},
|
||||
{
|
||||
number: 4,
|
||||
tooltip: 'Continue to output 4',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
|
||||
const lib = require('../../../../../methods/lib')();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
|
||||
args.inputs = lib.loadDefaultValues(args.inputs, details);
|
||||
const code = String(args.inputs.code);
|
||||
|
||||
const outputFilePath = `${getPluginWorkDir(args)}/script.js`;
|
||||
await fsp.writeFile(outputFilePath, code);
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
const func = require(outputFilePath);
|
||||
const response = await func(args);
|
||||
return response;
|
||||
};
|
||||
export {
|
||||
details,
|
||||
plugin,
|
||||
};
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import {
|
||||
IpluginDetails,
|
||||
IpluginInputArgs,
|
||||
IpluginOutputArgs,
|
||||
} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces';
|
||||
|
||||
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
|
||||
const details = (): IpluginDetails => ({
|
||||
name: 'Remove From Tdarr',
|
||||
description: `
|
||||
If this plugin is executed, then when the flow ends, the item will be
|
||||
removed from the Tdarr database and won't appear in Transcode Success or Error tables on the 'Tdarr' tab.
|
||||
Use the 'Delete File' plugin if you would like to delete the file from disk.
|
||||
`,
|
||||
style: {
|
||||
borderColor: 'red',
|
||||
},
|
||||
tags: '',
|
||||
isStartPlugin: false,
|
||||
pType: '',
|
||||
requiresVersion: '2.31.01',
|
||||
sidebarPosition: -1,
|
||||
icon: 'faTrash',
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
number: 1,
|
||||
tooltip: 'Continue to next plugin',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const plugin = (args: IpluginInputArgs): IpluginOutputArgs => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
args.variables.removeFromTdarr = true;
|
||||
|
||||
return {
|
||||
outputFileObj: args.inputFileObj,
|
||||
outputNumber: 1,
|
||||
variables: args.variables,
|
||||
};
|
||||
};
|
||||
export {
|
||||
details,
|
||||
plugin,
|
||||
};
|
||||
|
|
@ -74,6 +74,16 @@ const details = (): IpluginDetails => ({
|
|||
},
|
||||
tooltip: 'Specify request body',
|
||||
},
|
||||
{
|
||||
label: 'Log Response Body',
|
||||
name: 'logResponseBody',
|
||||
type: 'boolean',
|
||||
defaultValue: 'false',
|
||||
inputUI: {
|
||||
type: 'switch',
|
||||
},
|
||||
tooltip: 'Specify whether to log response body in the job report',
|
||||
},
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
|
|
@ -93,6 +103,7 @@ const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
|
|||
const requestUrl = String(args.inputs.requestUrl);
|
||||
const requestHeaders = JSON.parse(String(args.inputs.requestHeaders));
|
||||
const requestBody = JSON.parse(String(args.inputs.requestBody));
|
||||
const { logResponseBody } = args.inputs;
|
||||
|
||||
const requestConfig = {
|
||||
method,
|
||||
|
|
@ -104,6 +115,10 @@ const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => {
|
|||
try {
|
||||
const res = await args.deps.axios(requestConfig);
|
||||
args.jobLog(`Web request succeeded: Status Code: ${res.status}`);
|
||||
|
||||
if (logResponseBody) {
|
||||
args.jobLog(`Response Body: ${JSON.stringify(res.data)}`);
|
||||
}
|
||||
} catch (err) {
|
||||
args.jobLog('Web Request Failed');
|
||||
args.jobLog(JSON.stringify(err));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue