import rendi from "../../rendi.app.mjs";
import { ConfigurationError } from "@pipedream/platform";
import { parseObject } from "../../common/utils.mjs";
import { axios } from "@pipedream/platform";
import fs from "fs";
export default {
key: "rendi-run-ffmpeg-command",
name: "Run FFmpeg Command",
description: "Submit an FFmpeg command for processing with input and output file specifications. [See the documentation](https://docs.rendi.dev/api-reference/endpoint/run-ffmpeg-command)",
version: "0.0.1",
type: "action",
props: {
rendi,
inputFiles: {
type: "object",
label: "Input File URL(s)",
description: "Dictionary mapping file aliases to their publicly accessible paths, file name should appear in the end of the url, keys must start with 'in_'. You can use public file urls, google drive, dropbox, rendi stored files, s3 stored files, etc. as long as they are publicly accessible. [See the documentation](https://docs.rendi.dev/api-reference/endpoint/run-ffmpeg-command) for more information",
},
outputFiles: {
type: "object",
label: "Output File Name(s)",
description: "Dictionary mapping file aliases to their desired output file names, keys must start with 'out_'. [See the documentation](https://docs.rendi.dev/api-reference/endpoint/run-ffmpeg-command) for more information",
},
command: {
type: "string",
label: "FFmpeg Command",
description: "FFmpeg command string using {{alias}} placeholders for input and output files. `{{}}` is a reserved string, instead you can use `\\{\\{\\}\\}` , so for example `{{in_1}}` should be `\\{\\{in_1\\}\\}`. Example: `-i \\{\\{in_1\\}\\} \\{\\{out_1\\}\\}`",
},
maxCommandRunSeconds: {
type: "string",
label: "Max Command Run Seconds",
description: "Maximum allowed runtime in seconds for a single FFmpeg command, the default is 300 seconds",
optional: true,
},
waitForCompletion: {
type: "boolean",
label: "Wait for Completion",
description: "Set to `true` to poll the API in 3-second intervals until the command is completed",
optional: true,
reloadProps: true,
},
},
additionalProps() {
if (this.waitForCompletion) {
return {
downloadFilesToTmp: {
type: "boolean",
label: "Download Files to /tmp",
description: "Set to `true` to download the output files to the workflow's /tmp directory",
optional: true,
},
};
}
return {};
},
async run({ $ }) {
const inputFiles = parseObject(this.inputFiles);
const outputFiles = parseObject(this.outputFiles);
if (Object.keys(inputFiles).some((key) => !key.startsWith("in_"))) {
throw new ConfigurationError("Input file keys must start with 'in_'");
}
if (Object.keys(outputFiles).some((key) => !key.startsWith("out_"))) {
throw new ConfigurationError("Output file keys must start with 'out_'");
}
let response = await this.rendi.runFfmpegCommand({
$,
data: {
input_files: inputFiles,
output_files: outputFiles,
ffmpeg_command: this.command,
max_command_run_seconds: this.maxCommandRunSeconds,
},
});
if (this.waitForCompletion) {
const commandId = response.command_id;
const timer = (ms) => new Promise((res) => setTimeout(res, ms));
while (response?.status !== "SUCCESS" && response?.status !== "FAILED") {
response = await this.rendi.getFfmpegCommand({
$,
commandId,
});
await timer(3000);
}
if (response?.status === "SUCCESS" && this.downloadFilesToTmp) {
response.tmpFiles = [];
for (const value of Object.values(response.output_files)) {
const resp = await axios($, {
url: value.storage_url,
responseType: "arraybuffer",
});
const filename = value.storage_url.split("/").pop();
const downloadedFilepath = `/tmp/${filename}`;
fs.writeFileSync(downloadedFilepath, resp);
response.tmpFiles.push({
filename,
downloadedFilepath,
});
}
}
if (response?.error_message) {
throw new ConfigurationError(response.error_message);
}
}
$.export("$summary", `FFmpeg command ${this.waitForCompletion
? "submitted and completed"
: "submitted"} successfully`);
return response;
},
};