header={ "recipe_version": "1.10", "title": "Instagram video", "description": "Convert a video to the instagram share screen ratio and time limit", "category": "video", "chef": "BeatRig", "spice": "BQ==:G+Hy36kJcnQQYYavb/JV2Il6eCYr6h4hRJHCF9rRbhX5BcazL6cuTgUMdz0xA50VWA5PmmV0Q7aWkiUtNFtbk991l+t0e8IBXMReMIT1GNjnZZDv7n2YRAo0xcSQbY0ixjG6LLH4wGKnspKAoA/v69iLn6N2IH0XzIZwTGdHSo4=", "palette": "Velvet Cake", "flavour": "lEM7evufJIDvc6sefJlH8F+Uqr5hrIMUpiZ5sJ3x5Mp/ByprgaufRakE0HAm5qH7suEok0tlL24HM8sNWbaV1nxTR4hxkveG7qisQ+yj1ZZTJeB/UccN19lqiMNCDDT1Wp0x8yGcr9BJUzysp05kY1ypS85rT0yVcO1WPViyoiE=", "time": 1694382987, "core_version": "0.5.7", "magnetron_version": "1.0.261", "type": "default", "os": "windows,macOS", "cmds": "ffprobe,ffmpeg", "functions": "main", "dependencies": "ffprobe,ffmpeg", "tags": "video,instagram,square", "uuid": "d01828bc987346378739461d75cae89a", "instructions": "Drop file(s) to crop them to a square aspect ratio and cut to a stories or reels length" }; //---------------------------------------------------------------------------- var config = { "scale": 1, "framerate": 0, "makeMono": false, "crf": 40, "parts": 1, "makeMono": true, "partItt": 0, "cutLength": 0, "sizeRatio": 1, "totalsec": 0, "fps": 0, "maxItt": 4, "maxSize": 4 * 1024 * 1024 * 1024, // to 4 gb "mincrf": 20, "maxcrf": 55, "minframerate": 10, "maxframerate": 25, "minScale": 1, "maxScale": 4, }; //---------------------------------------------------------------------------- function cmd_callback(cmd_name, cmd_output) { if(cmd_name == "ffmpeg" && cmd_output.length > 0) { var progress = calculateSeconds(findSubString(cmd_output, "time=", " bitrate")); progress = Math.round(progress / config.cutLength /*config.totalsec*/ * 100); setProgress(progress); } return { "terminate": isCanceled(), "input": "" // send console input }; } function calculateSeconds(dur_tc) { var hours = (dur_tc.substring(0, 2)); var minutes = (dur_tc.substring(3, 3+2)); var seconds = (dur_tc.substring(6, 6+2)); if (hours.substring(0, 1) == "0") hours = hours.substring(1,2); if (minutes.substring(0, 1) == "0") minutes = minutes.substring(1,2); if (seconds.substring(0, 1) == "0") seconds = seconds.substring(1,2); hours = parseInt(hours); minutes = parseInt(minutes); seconds = parseInt(seconds); totalsec = (hours * 60 * 60) + (minutes * 60) + seconds; return totalsec; } function getFileProps(infile) { var probeoutput = cmd("ffprobe", [infile]); echo(probeoutput); var dur_tc = findSubString(probeoutput, "Duration: ", ", s"); config.totalsec = calculateSeconds(dur_tc); config.fps = parseInt(findSubString(probeoutput, "kb/s, ", " fps")); echo("dur:" + config.totalsec); echo("fps:" + config.fps); return dur_tc; } function setupArgs(infile, cutItt) { var args1 = ["-y", "-i", infile]; if (config.parts > 1) { echo("start time:" + cutItt * config.cutLength); args1.push("-ss"); args1.push(cutItt * config.cutLength); } if (cutItt < config.parts - 1) { echo("length:" + config.cutLength); args1.push("-t"); args1.push(config.cutLength); } args1.push("-vcodec"); args1.push("libx264"); if (config.framerate > 0) { echo("frame rate:" + config.framerate); args1.push("-r"); args1.push(config.framerate); } echo("compression:" + config.crf); args1.push("-crf"); args1.push(config.crf); args1.push("-preset"); args1.push("slow"); args1.push("-c:a"); args1.push("aac"); if (config.makeMono) { echo("mono"); args1.push("-ac"); args1.push("1"); } args1.push("-movflags"); args1.push("+faststart"); { args1.push("-filter_complex"); var filterArg="[0]crop=min(iw\\,ih):min(iw\\,ih),format=yuv420p[out1]"; if (gvar.isWindows) // needs quotes filterArg = "\"" + filterArg + "\""; args1.push(filterArg); echo("filter:" + filterArg); // map the inputs args1.push("-map"); args1.push("[out1]"); args1.push("-map"); args1.push("0:a?"); } args1.push(config.outfile); return args1; } //---------------------------------------------------------------------------- function setOutputSettings() { var partRatio = config.sizeRatio / config.parts; config.makeMono = (partRatio > 1.5); config.crf = config.mincrf; // some size ratio based presets if (partRatio > 8) { config.scale = config.maxScale; config.framerate = config.minframerate; } else if (partRatio > 4) { config.scale = 2; config.framerate = Math.min(Math.max(parseInt(config.fps / (partRatio / config.scale)), config.minframerate), config.maxframerate); } else if (partRatio > 2) config.scale = 2; } //---------------------------------------------------------------------------- function main() { // check if ffmpeg is installed if (fileExists(getAllowedApps("ffmpeg")) == false) abort("no ffmpeg installed"); if (fileExists(getAllowedApps("ffprobe")) == false) abort("no ffprobe installed"); setMainMessage("starting"); setProgress(0); var now = getCurrentEpoch(); // secs since 1 jan 1970 var files = getFiles(); config.tag = "-[insta]"; var numFiles = files.length; for (i = 0; i < Math.max(numFiles, 1); i++) { // base path var infile = numFiles > 0 ? files[i].path : ""; var filesize = getFileBytes(infile); var infileinfo = getPathInfo(numFiles > 0 ? infile : folders.output); var ouputFolderPath = numFiles > 0 ? infileinfo.folder : folders.output; echo(infile); echo("file size:" + bytesToDescription(filesize)); setFileIcon(files[i].path, files[i].index, "HourglassHalf"); setFileIconColor(files[i].path, files[i].index, "FF5299D3"); setFileStatus(files[i].path, files[i].index, "busy"); var maxClipLength = 60; // limits 15 sec or 60 sec var options = ["15 sec Stories","60 sec Reels"]; config.videotype = options[1]; var dialog_width = 600; form1 = { "header" : { "type" : "text", "label" : "This process will convert videos to instagram spec", "just" : "l", "bounds" : { "x": 10, "y" : 5, "w" : dialog_width, }, }, "file1_header" : { "type" : "text", "label" : "source file", "just" : "l", "bounds" : { "w" : 120, }, }, "inputfile" : { "type" : "fileselect", "path" : infile, "editable" : false, "dir" : false, "saving" : false, "label" : "source file", "bounds" : { "x" : 130, "y" : -1, "w" : dialog_width - 130, }, }, "param_header" : { "type" : "text", "label" : "Video type", "just" : "l", "bounds" : { "w" : 120, }, }, "maxClipLength" : { "type" : "combobox", "default" : config.videotype, "items" : options, "label" : "Select length:", "bounds" : { "x" : 130, "y" : -1, "w" : dialog_width - 130, } }, "file2_header" : { "type" : "text", "label" : "output folder", "just" : "l", "bounds" : { "w" : 120, }, }, "outputfolder" : { "type" : "fileselect", "path" : ouputFolderPath, "editable" : false, "dir" : true, "saving" : true, "label" : "select output folder", "bounds" : { "x" : 130, "y" : -1, "w" : dialog_width - 130, }, }, "tag" : { "type" : "textedit", "default" : config.tag, "label" : "add file name tag:", "bounds" : { "x" : dialog_width/2, "y" : -1, "w" : dialog_width/2 } }, "okay" : { "type" : "button", "label" : "Start", "bounds" : { "w" : dialog_width/2 - 10, }, "returns" : 1 }, "cancel" : { "type" : "button", "label" : "cancel", "bounds" : { "y": -1, "x": dialog_width/2 + 10, "w" : dialog_width/2 - 10 }, "returns" : 0 }, }; var r = dialog(form1); config.cutLength = 60; if (r.maxClipLength == options[0]) config.cutLength = 15; config.videotype = r.maxClipLength; config.tag = r.tag; infile = r.inputfile; var pathinfo = getPathInfo(infile); var ouputFolderPath = numFiles > 0 ? infileinfo.folder : folders.output; if (r.okay == 1) { getFileProps(infile); // calc the duration in sec if (config.totalsec <= 0) abort("no content found in this file"); config.parts = (config.totalsec / config.cutLength); if (parseInt(config.parts) < config.parts) config.parts = parseInt(config.parts) + 1; else config.parts = parseInt(config.parts); setOutputSettings(); // compression setup config.framecount = config.fps * config.totalsec; echo("framecount:" + config.framecount); // for progress calculation setProgress(101); var newfilesize = 0; config.partItt = 0; while (config.partItt < config.parts) { if (config.parts > 1) { config.outfile = r.outputfolder + gvar.pss + pathinfo.basename + r.tag + "-[" + (config.partItt + 1) + "_" + config.parts + "]." + pathinfo.ext; if (fileExists(config.outfile)) { var form2 = { "overwrite" : { "type" : "button", "label" : "overwrite", "returns" : 1 }, "cancel" : { "type" : "button", "label" : "cancel", "returns" : 0 } }; if (dialog(header.title, "file already exists\n" + config.outfile, "w", form2).cancel == 1) abort("cancelled"); } } else config.outfile = r.outputfolder + gvar.pss + pathinfo.basename + r.tag + "." + pathinfo.ext; var msg = ("busy please wait"); setMainMessage(msg); echo(msg); var args1 = setupArgs(infile, config.partItt); echo(cmd("ffmpeg", args1)); // perform cmd newfilesize = getFileBytes(config.outfile); echo("new file size:" + bytesToDescription(newfilesize)); if (newfilesize <= 0) config.partItt = config.parts; // stop if(isCanceled()) abort("cancelled"); config.partItt++; } if (newfilesize <= 0) { setFileIcon(files[i].path, files[i].index, "ExclamationTriangle"); setFileIconColor(files[i].path, files[i].index, "FFb11414"); setFileStatus(files[i].path, files[i].index, "can't encode this file", "w"); } else { setFileIcon(files[i].path, files[i].index, "CheckCircleO"); setFileIconColor(files[i].path, files[i].index, "FF3aac4d"); setFileStatus(files[i].path, files[i].index, "done"); } } else i = numFiles; // end-loop } var then = getCurrentEpoch(); // secs since 1 jan 1970 setMainMessage("done in " + (then - now) + " sec"); echo(objectToString(config)); setProgress(100); } function dialog_callback(props) { if (props["name"] == "inputfile") { var info = getPathInfo(props["path"]); var newprops = { "outputfolder" : { "path" : info.folder } }; dialog(newprops); } }