Cut video X/Twitter
Don't let the 2-minute, 20-second limit stop you from sharing longer videos on default accounts at x/twitter.
This recipe splits up a video to the x/twitter file share time limit. To solve the challenge of sharing longer video files on any account.
no more pop-ups; "Your video is too long. Try to keep it 140 seconds or less"
Download the recipe from the extended collection here:
https://magnetron.app/recipes/cut-video-x-twitter/
For Windows & macOS
You can run this recipe with a magnetron.app license key.
With magnetron.dev you can also use the source to create your own custom processes.
Take a closer look at the source below:
(added Sept 2023)
header={
"recipe_version": "1.9",
"title": "Cut video X/Twitter",
"description": "Cut a video into 2:20 pieces for easy sharing",
"category": "video",
"chef": "BeatRig",
"spice": "BQ==:G+Hy36kJcnQQYYavb/JV2Il6eCYr6h4hRJHCF9rRbhX5BcazL6cuTgUMdz0xA50VWA5PmmV0Q7aWkiUtNFtbk991l+t0e8IBXMReMIT1GNjnZZDv7n2YRAo0xcSQbY0ixjG6LLH4wGKnspKAoA/v69iLn6N2IH0XzIZwTGdHSo4=",
"palette": "Blueberry Slate",
"flavour": "1ozXUgOnXRCut+6SM2W39uw+zNUj+t8Xg3STQ+GgYxhtH4qAJwU0UaahzrcDVAjScnXgDeIZh/kYEZkY6w2r7p4zQKoUIajUUxNIcIYfz1SjhOxgLhXVEwT66XydUhOgx8YIiQs7HbuYEtvD8Qnfi2V9qZhlcS+bO5Z/lL0S8wI=",
"time": 1694478580,
"core_version": "0.5.7",
"magnetron_version": "1.0.261",
"cmds": "ffprobe,ffmpeg",
"type": "default",
"os": "windows,macOS",
"functions": "main,onConfig,onAbout",
"dependencies": "ffprobe,ffmpeg",
"uuid": "514f947bc71d49e5bbf433f6f4922b7c",
"instructions": "Drop files and press run to cut videos into 2:20 min parts",
"tags": "video,share limit,2m20,@:20,cut video"
};
//----------------------------------------------------------------------------
var config = {
"parts": 1,
"partItt": 0,
"cutLength": 140,
"totalsec": 0,
};
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.totalsec * 100);
setProgress(progress);
setMainMessage(progress + "%");
}
return {
"terminate": isCanceled(),
};
}
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 setDur(infile)
{
var probeoutput = cmd("ffprobe", [infile]);
echo(probeoutput);
var dur_tc = findSubString(probeoutput, "Duration: ", ", s");
config.totalsec = calculateSeconds(dur_tc);
echo("dur:" + config.totalsec);
}
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("-preset");
args1.push("slow");
args1.push("-movflags");
args1.push("+faststart");
args1.push(config.outfile);
return args1;
}
//----------------------------------------------------------------------------
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();
var infile = "";
var numFiles = files.length;
if (numFiles<= 0)
abort("drop some files first");
for (i = 0; i < numFiles && isCanceled() == false; i++)
{
// base path
infile = files[i].path;
var pathinfo = getPathInfo(infile);
setFileIcon(files[i].path, files[i].index, "HourglassHalf");
setFileIconColor(files[i].path, files[i].index, "FF5299D3");
setFileStatus(files[i].path, files[i].index, "busy");
// calc the duration in sec
setDur(infile);
if (config.totalsec > 0)
{
config.parts = (config.totalsec / config.cutLength);
if (parseInt(config.parts) < config.parts)
config.parts = parseInt(config.parts) + 1;
else config.parts = parseInt(config.parts);
setProgress(101);
var userOuputPath = pathinfo.folder + gvar.pss + pathinfo.basename + "-x-[magnetron.app]." + pathinfo.ext;
var fileNames = [];
if (config.parts > 1)
{ // create all file names (cluster dialogs together)
config.partItt = 0;
while (config.partItt < config.parts)
{
var pathinfoOut = getPathInfo(userOuputPath);
config.outfile = pathinfoOut.folder + gvar.pss + pathinfoOut.basename + "-[" + (config.partItt + 1) + "_" + config.parts + "].mp4";
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" + pathinfoOut.basename + "." + pathinfoOut.ext, "w", form2).cancel == 1)
{
setMainMessage("cancelled");
abort("cancelled");
}
}
fileNames.push(config.outfile);
config.partItt++;
}
}
else
{
config.outfile = userOuputPath;
fileNames.push(config.outfile);
}
var newfilesize = 0;
config.partItt = 0;
while (config.partItt < config.parts)
{ // main process
var msg = ("busy please wait");
setMainMessage(msg);
echo(msg);
config.outfile = fileNames[config.partItt];
var args1 = setupArgs(infile, config.partItt);
echo(cmd("ffmpeg", args1)); // perform cmd
newfilesize = getFileBytes(config.outfile);
echo("new file size:" + bytesToDescription(newfilesize));
if(isCanceled())
{
setMainMessage("cancelled");
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 setFileStatus(files[i].path, files[i].index, "failed to read");
}
var then = getCurrentEpoch(); // secs since 1 jan 1970
setMainMessage("done in " + (then - now) + " sec");
echo(objectToString(config));
setProgress(100);
}
function onAbout()
{
dialog(header.title, header.description + "\n\nby " + header.chef, "i" );
}
function onConfig()
{
dialog(header.title, "No config needed. This recipe will convert video files from the file list.");
}
function dialog_callback(props)
{
}