Skip to content

Instantly share code, notes, and snippets.

@weskerty
Last active March 25, 2026 18:02
Show Gist options
  • Select an option

  • Save weskerty/8c0c3f5801d2042869c79b57066c4ef9 to your computer and use it in GitHub Desktop.

Select an option

Save weskerty/8c0c3f5801d2042869c79b57066c4ef9 to your computer and use it in GitHub Desktop.
const fs = require('fs').promises;
const path = require('path');
const { promisify } = require('util');
const { exec: execCallback } = require('child_process');
const { bot } = require('../lib');
const exec = promisify(execCallback);
const fmtTime = (s) => {
if (!s || isNaN(s)) return "0";
return `${Math.floor(s/60)}:${Math.floor(s%60).toString().padStart(2,'0')}`;
};
class MessageUtils {
static getVideoDetails(q) {
try {
const m = q.message?.message || q;
if (m?.videoMessage) return { isVideo: true, mimetype: m.videoMessage.mimetype || 'video/mp4', fileLength: m.videoMessage.fileLength ? parseInt(m.videoMessage.fileLength) : null };
if (m?.documentMessage) { const mt = m.documentMessage.mimetype || ''; return { isVideo: mt.startsWith('video/'), mimetype: m.documentMessage.mimetype, fileLength: m.documentMessage.fileLength ? parseInt(m.documentMessage.fileLength) : null }; }
return { isVideo: false };
} catch { return { isVideo: false }; }
}
}
const SEG = 89;
bot(
{ pattern: 'split ?(.*)', fromMe: true, desc: 'Split video Status', type: 'whatsapp' },
async (message, match, ctx) => {
const reply = async (text, err = false) => message.send(`${err ? '❌' : '✅'} ${text}`, { quoted: message.data });
try {
const q = message.reply_message;
if (!q) return reply('Cita un video', true);
const vd = MessageUtils.getVideoDetails(q);
if (!vd.isVideo) return reply('El archivo citado no es video', true);
try { await exec('ffmpeg -version'); } catch { return reply('FFmpeg no instalado', true); }
const sid = `split_${Date.now()}`;
const tmpDir = `/tmp/${sid}`;
await fs.mkdir(tmpDir, { recursive: true });
try {
const buf = await q.downloadMediaMessage();
if (!buf) return reply('No se pudo descargar el video', true);
const inPath = path.join(tmpDir, 'in.mp4');
await fs.writeFile(inPath, buf);
const durRes = await exec(`ffprobe -v quiet -show_entries format=duration -of csv=p=0 "${inPath}"`);
const duration = parseFloat(durRes.stdout.trim());
const outPattern = path.join(tmpDir, 'seg_%03d.mp4');
await exec([
'ffmpeg', '-i', `"${inPath}"`,
'-c', 'copy',
'-map', '0',
'-segment_time', SEG.toString(),
'-f', 'segment',
'-reset_timestamps', '1',
'-avoid_negative_ts', 'make_zero',
`"${outPattern}"`
].join(' '));
const files = (await fs.readdir(tmpDir))
.filter(f => f.startsWith('seg_') && f.endsWith('.mp4'))
.sort()
.map(f => path.join(tmpDir, f));
if (!files.length) return reply('No se generaron segmentos', true);
for (let i = 0; i < files.length; i++) {
const segBuf = await fs.readFile(files[i]);
const t0 = i * SEG;
const t1 = Math.min((i+1) * SEG, duration || (i+1)*SEG);
await message.send(segBuf, {
fileName: `parte_${String(i+1).padStart(2,'0')}_de_${files.length}.mp4`,
mimetype: 'video/mp4',
quoted: message.data,
caption: `Parte ${i+1}/${files.length} | ${fmtTime(t0)} - ${fmtTime(t1)}`
}, 'video');
if (i < files.length - 1) await new Promise(r => setTimeout(r, 1000));
}
} finally {
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});
}
} catch (e) {
await reply(`Error: ${e.message}`, true);
}
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment