diff --git a/commands/utility/download.js b/commands/utility/download.js index eb707dfc..f60acdc0 100644 --- a/commands/utility/download.js +++ b/commands/utility/download.js @@ -25,11 +25,15 @@ export default { .setRequired(false)) .addBooleanOption(option => option.setName('compress') - .setDescription('Compress the video?') + .setDescription('Compress the video.') + .setRequired(false)) + .addBooleanOption(option => + option.setName('autocrop') + .setDescription('Autocrop borders on videos. Ignored when using compress option.') .setRequired(false)) .addBooleanOption(option => option.setName('description') - .setDescription('Include the video description?') + .setDescription('Include the video description.') .setRequired(false)), category: 'utility', alias: ['dl'], @@ -41,6 +45,7 @@ export default { const format = args.format; maxFileSize = await utils.getMaxFileSize(interaction.guild); interaction.doCompress = args.compress; + interaction.doAutocrop = args.autocrop; await interaction.deferReply({ ephemeral: false }); @@ -187,14 +192,21 @@ async function download(url, interaction, originalInteraction, format = undefine return; } - // If the video format is not one compatible with Discord, reencode it. - const bannedFormats = ['hevc']; - const codec = await utils.getVideoCodec(output); - - if (bannedFormats.includes(codec)) { + // If the video format is not one compatible with Discord, reencode it unless autocrop is choosen in which case it gets reencoded anyway. + if (!interaction.doAutocrop) { + const bannedFormats = ['hevc']; + const codec = await utils.getVideoCodec(output); + + if (bannedFormats.includes(codec)) { + const oldOutput = output; + output = `${os.tmpdir()}/264${file}`; + await utils.ffmpeg(['-i', oldOutput, '-vcodec', 'libx264', '-acodec', 'aac', output]); + } + } + else if (interaction.doAutocrop && !compressInteraction.doCompress) { const oldOutput = output; - output = `${os.tmpdir()}/264${file}`; - await utils.ffmpeg(['-i', oldOutput, '-vcodec', 'libx264', '-acodec', 'aac', output]); + output = `${os.tmpdir()}/autocrop${file}`; + await utils.autoCrop(oldOutput, output); } const fileStat = fs.statSync(output); diff --git a/utils/videos.js b/utils/videos.js index 89b90454..d162125f 100644 --- a/utils/videos.js +++ b/utils/videos.js @@ -11,6 +11,7 @@ export default { getVideoCodec, getVideoSize, getMaxFileSize, + autoCrop, }; async function downloadVideo(urlArg, output, format = `bestvideo[height<=?${ytdlpMaxResolution}]+bestaudio/best`) { await new Promise((resolve, reject) => { @@ -70,7 +71,7 @@ async function stringIsAValidurl(s) { async function compressVideo(input, output, preset) { await new Promise((resolve, reject) => { - execFile('./bin/HandBrakeCLI', ['-i', input, '-Z', preset, '-o', `${os.tmpdir()}/${output}`], (err, stdout, stderr) => { + execFile('./bin/HandBrakeCLI', ['-i', input, '-Z', preset, '--turbo', '--optimize', '-o', `${os.tmpdir()}/${output}`], (err, stdout, stderr) => { if (err) { reject(stderr); } @@ -133,4 +134,40 @@ async function getMaxFileSize(guild) { break; } }); -} \ No newline at end of file +} + +async function autoCrop(input, output) { + return await new Promise((resolve, reject) => { + let ffprobeInput = input; + if (process.platform === 'win32') { + // ffprobe 'movie=' options does not like windows absolute path + ffprobeInput = input.replace(/\\/g, '/').replace(/\:/g, '\\\\:'); + } + + execFile('ffprobe', + ['-f', 'lavfi', '-i', `movie=${ffprobeInput},cropdetect`, '-show_entries', + 'packet_tags=lavfi.cropdetect.x1,lavfi.cropdetect.x2,lavfi.cropdetect.y1,lavfi.cropdetect.y2,lavfi.cropdetect.w,lavfi.cropdetect.h,lavfi.cropdetect.x,lavfi.cropdetect.y', + '-read_intervals', '%+#10', '-hide_banner', '-print_format', 'json'], async (err, stdout, stderr) => { + if (err) { + reject(stderr); + } + if (stderr) { + console.error(stderr); + } + const packets = JSON.parse(stdout).packets; + + for (let i = 0; i < packets.length; i++) { + const element = packets[i]; + + if (element.tags) { + const cropdetect = element.tags; + await ffmpeg(['-i', input, '-vf', `crop=${cropdetect['lavfi.cropdetect.w']}:${cropdetect['lavfi.cropdetect.h']}:${cropdetect['lavfi.cropdetect.x']}:${cropdetect['lavfi.cropdetect.y']}`, '-vcodec', 'libx264', '-acodec', 'aac', output]) + break; + } + } + + console.log(NODE_ENV === 'development' ? stdout : null); + resolve(); + }); + }); +}