Haha-Yes/commands/utility/vid2gif.js

146 lines
4.6 KiB
JavaScript
Raw Normal View History

import { SlashCommandBuilder } from 'discord.js';
2022-06-17 07:31:58 +02:00
import utils from '../../utils/videos.js';
2022-06-17 01:25:35 +02:00
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { execFile } from 'node:child_process';
2022-09-14 11:31:19 +02:00
const { NODE_ENV } = process.env;
2024-08-31 15:44:31 +02:00
const ytdlpFormat = 'bestvideo[height<=?480]/best';
2022-06-17 01:25:35 +02:00
export default {
data: new SlashCommandBuilder()
.setName('vid2gif')
.setDescription('Convert your video into a gif.')
.addStringOption(option =>
option.setName('url')
.setDescription('URL of the video you want to convert')
2023-04-15 23:49:34 +02:00
.setRequired(true))
2023-12-26 16:39:34 +01:00
.addIntegerOption(option =>
option.setName('quality')
.setDescription('Quality of the gif conversion. Default 70. Number between 1 and 100')
.setRequired(false))
2024-06-30 02:01:57 +02:00
.addIntegerOption(option =>
option.setName('fps')
.setDescription('Change the speed at which the gif play at. Default 20. Number between 1 and 100.')
.setRequired(false))
2024-07-08 23:50:28 +02:00
.addBooleanOption(option =>
option.setName('autocrop')
.setDescription('Autocrop borders on gif.')
.setRequired(false))
2023-04-15 23:49:34 +02:00
.addBooleanOption(option =>
option.setName('noloop')
.setDescription('Stop the gif from looping')
.setRequired(false)),
category: 'utility',
alias: ['v2g', 'togif'],
integration_types: [0, 1],
async execute(interaction, args) {
2022-06-17 01:25:35 +02:00
await interaction.deferReply({ ephemeral: false });
2023-04-19 17:05:35 +02:00
const maxFileSize = await utils.getMaxFileSize(interaction.guild);
2022-09-01 01:43:59 +02:00
const url = args.url;
2023-12-26 16:39:34 +01:00
let quality = args.quality;
2024-06-30 02:01:57 +02:00
if (quality) {
if (quality <= 0) {
quality = 1;
}
else if (quality > 100) {
quality = 100;
}
2023-12-26 16:39:34 +01:00
}
2024-07-11 07:19:25 +02:00
2024-06-30 02:01:57 +02:00
if (args.fps) {
if (args.fps <= 0) {
args.fps = 1;
}
else if (args.fps > 100) {
args.fps = 100;
}
2023-12-26 16:39:34 +01:00
}
2022-06-17 01:25:35 +02:00
if (!await utils.stringIsAValidurl(url)) {
console.error(`Not a url!!! ${url}`);
return interaction.editReply({ content: '❌ This does not look like a valid url!', ephemeral: true });
}
2024-08-31 15:44:31 +02:00
const aproxFileSize = await utils.getVideoSize(url, ytdlpFormat);
console.log(aproxFileSize);
if (aproxFileSize > 4) {
return interaction.editReply('The file you are trying to convert is too big! Limit is 4 MB');
};
2024-08-31 15:44:31 +02:00
utils.downloadVideo(url, interaction.id, ytdlpFormat)
2022-06-17 01:25:35 +02:00
.then(async () => {
const file = fs.readdirSync(os.tmpdir()).filter(fn => fn.startsWith(interaction.id));
2024-07-08 23:50:28 +02:00
let output = `${os.tmpdir()}/${file}`;
if (args.autocrop) {
const oldOutput = output;
output = `${os.tmpdir()}/autocrop${file}`;
await utils.autoCrop(oldOutput, output);
}
2022-06-17 01:25:35 +02:00
const gifskiOutput = output.replace(path.extname(output), '.gif');
const gifsicleOutput = output.replace(path.extname(output), 'gifsicle.gif');
2022-06-17 02:14:14 +02:00
// Extract every frame for gifski
await utils.ffmpeg(['-i', output, `${os.tmpdir()}/frame${interaction.id}%04d.png`]);
2022-06-17 02:14:14 +02:00
// Make it look better
2024-06-30 02:01:57 +02:00
await gifski(gifskiOutput, `${os.tmpdir()}/frame${interaction.id}*`, quality, args.fps);
2022-06-17 02:14:14 +02:00
// Optimize it
2023-04-15 23:49:34 +02:00
await gifsicle(gifskiOutput, gifsicleOutput, args.noloop);
2022-06-17 01:25:35 +02:00
const fileStat = fs.statSync(gifsicleOutput);
const fileSize = fileStat.size / 1000000.0;
if (fileSize > 25) {
2022-06-17 01:25:35 +02:00
await interaction.deleteReply();
2022-07-01 21:24:39 +02:00
await interaction.followUp('❌ Uh oh! The video once converted is too big!', { ephemeral: true });
2022-06-17 01:25:35 +02:00
}
2023-04-19 17:05:35 +02:00
else if (fileSize > maxFileSize) {
2022-06-17 01:25:35 +02:00
const fileURL = await utils.upload(gifsicleOutput)
.catch(err => {
console.error(err);
});
2023-04-19 17:05:35 +02:00
await interaction.editReply({ content: ` File was bigger than ${maxFileSize} mb. It has been uploaded to an external site.\n${fileURL}`, ephemeral: false });
2022-06-17 01:25:35 +02:00
}
else {
await interaction.editReply({ files: [gifsicleOutput], ephemeral: false });
}
});
},
};
2024-06-30 02:01:57 +02:00
async function gifski(output, input, quality, fps) {
2022-06-17 01:25:35 +02:00
return await new Promise((resolve, reject) => {
// Shell: true should be fine as no user input is being passed
2024-06-30 02:01:57 +02:00
execFile('gifski', ['--quality', quality ? quality : 70, '--fps', fps ? fps : 20, '-o', output, input], { shell: true }, (err, stdout, stderr) => {
2022-06-17 01:25:35 +02:00
if (err) {
reject(stderr);
}
if (stderr) {
console.error(stderr);
}
2022-09-14 11:31:19 +02:00
console.log(NODE_ENV === 'development' ? stdout : null);
2022-09-12 11:33:25 +02:00
resolve();
2022-06-17 01:25:35 +02:00
});
});
}
2023-04-15 23:49:34 +02:00
async function gifsicle(input, output, loop = false) {
2022-06-17 01:25:35 +02:00
return await new Promise((resolve, reject) => {
// Shell: true should be fine as no user input is being passed
execFile('gifsicle', ['--colors', '256', loop ? '--no-loopcount' : '', '-i', input, '-o', output], { shell: true }, (err, stdout, stderr) => {
2022-06-17 01:25:35 +02:00
if (err) {
reject(stderr);
}
if (stderr) {
console.error(stderr);
}
2022-09-14 11:31:19 +02:00
console.log(NODE_ENV === 'development' ? stdout : null);
2022-09-12 11:33:25 +02:00
resolve();
2022-06-17 01:25:35 +02:00
});
});
}