This code works. It throws an error on the first getClipFile, because ffmpeg hasn't completed. I kind of understand that, but also it's running again somehow?
I want to make it run once, after the my bash script has completed.
I also cannot understand how to simply wrap it all in a function and just wait on it.
Thanks!
The JS:
const gameModel = require('../models/gameSchema')
const fs = require("fs");
const { MessageAttachment } = require('discord.js')
const { exec } = require('child_process')
module.exports = {
name: 'contestsubmit',
async execute(message, args, client, Discord ) {
const channel = await client.channels.cache.get('830145067738071092')
const bashScript = "/home/ubuntu/smashbot/utils/clipper.sh"
const youtubeURL = args[0]
const clipStart = +(args[1].split(':').reduce((acc,time) => (60 * acc) + +time))
const clipEnd = +(args[2].split(':').reduce((acc,time) => (60 * acc) + +time))
const clipID = message.id
const clipType = "Play"
let i = "";
message.delete()
const myShellScript = exec(`sh ${bashScript} "${youtubeURL}" ${clipType} ${clipStart} ${clipEnd} ${clipID}`)
myShellScript.stdout.on('data', (data) => {
i += '23'
console.log('i==================',i)
getClipFile()
})
myShellScript.stderr.on('data', (data) => {
console.error(data);
})
const getClipFile = () => {
fs.readFile(`/home/ubuntu/smashbot/clip-${clipType}-${clipID}.mp4`, (err, data) => {
if (err) {
console.error(err)
return
}
const stats = fs.statSync(`/home/ubuntu/smashbot/clip-${clipType}-${clipID}.mp4`);
const fileSizeInBytes = stats.size
console.log('FILE SIZE', fileSizeInBytes)
const videoAttachment = new MessageAttachment(data, `${clipID}.mp4`)
channel.send(`**Nominated for Play of the Week!**`, videoAttachment)
console.log('sending clip?')
})}
}}
The bash:
#!/bin/bash
if [ $# -ne 5 ]; then
echo 'Illegal number of parameters. Needs parameters:'
echo 'Usage:'
echo './clipper.sh YOUTUBE_URL FILE GAME_START GAME_END'
echo
echo 'Parameters:'
echo ' - YOUTUBE_URL No "https://", just the youtube.com'
echo ' - CLIP_TYPE: Clip Type: Choose "Play" or "Life"'
echo ' - CLIP_START: Start time of game in seconds'
echo ' - CLIP_END: Emd time of game in seconds'
echo ' (video format and quality options etc.)'
exit 1
fi
YOUTUBE_URL="$1"
CLIP_TYPE="$2"
CLIP_START="$3"
CLIP_END="$4"
INDEX="$5"
# Filename of the source video (without extension)
BASENAME="${CLIP_TYPE%.*}"
# Extension for the video parts
EXTENSION="mp4"
# Filename of the next video part
NEXTFILENAME="clip-$BASENAME-$INDEX.$EXTENSION"
# Encode next part
echo ffmpeg -ss $CLIP_START -i youtube-dl -f 22 --get-url $YOUTUBE_URL -to $CLIP_END -c copy $NEXTFILENAME
ffmpeg -i $(youtube-dl -f 22 --get-url $YOUTUBE_URL) -ss $CLIP_START -to $CLIP_END -c copy $NEXTFILENAME
echo "Clipped $CLIP_TYPE: starts at $CLIP_START and ends at $CLIP_END"
You need to wait for the end event before calling getClipFile(). You're calling it every time you get a buffer of data.
myShellScript.stdout.on('data', (data) => {
i += '23'
console.log('i==================',i)
});
myShellScript.stdout.on('end', () => getClipFile());
Related
I had an ffmpeg script that allow me to detect black frames from a video file sample from the bottom.
and i want to create a javascript code that will allow me to do the same function sample from the bottom but its not working.
original code from ffmpeg script:
`ffmpeg -i LKE-BLACK.mp4 -vf "blackdetect=d=0.5:pix_th=0.10" -an -f null - 2>&1 | findstr blackdetect > output.txt
node script:
var fs = require('fs');
const ffmpeg = require("ffmpeg.js");
var createStream = fs.createWriteStream("data.txt");
createStream.end();
const transcode = async ({ target: { files } }) => {
message.innerHTML = 'Loading ffmpeg-core.js';
await ffmpeg.load();
message.innerHTML = 'Start transcoding';
await ffmpeg.transcode('-i', 'LKE-BLACK.mp4', "-vf", "blackdetect=d=0.5:pix_th=0.10", '-an', '-f', 'null - 2>&1', );
message.innerHTML = 'Complete transcoding';
fs.writeFile("data.txt", function (err) {
if (err) throw err;
console.log('File is created successfully.');
});
}
I'm trying to add a command code under the command file, but i'm unable to get it to work. The problem arises at this line => if (command == 'checkin' || command == 'ch') {
client.commands.get('chk').execute(message).
Without that line, the code works fine with the other 2 commands. I think it has to do with the async function but I'm not sure how to solve this problem. I don't want to include the whole chunk of code in the main file either, as it gets very long and cluttered. I'm new to coding, so it might be something I can't understand yet - please help me!
bot.js (the main .js file)
const { token, prefix } = require('./config.json');
const fs = require('fs');
const db = require('quick.db');
const ms = require('parse-ms-2')
const { Client, Intents, Message, Collection } = require("discord.js");
const client = new Client({
intents: [
Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MESSAGES
]
});
client.commands = new Discord.Collection();
// filter commands
const commandFiles = fs.readdirSync('./commands/').filter(file => file.endsWith('.js'));
// fetch commands
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.name, command);
}
client.once("ready", () => {
console.log("online.");
client.user.setPresence({ activties: [{ name: 'with commands' }] });
})
client.on('messageCreate', async message => {
// definite command components
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase()
if (command == 'help' || command == 'h') {
client.commands.get('help').execute(message)
}
if (command == 'bal') {
client.commands.get('bal').execute(message)
}
if (command == 'checkin' || command == 'ch') {
client.commands.get('chk').execute(message)
}
})
client.login(token)
chk.js (where the command is)
const db = require('quick.db')
const nm = require('parse-ms-2')
module.exports = {
name: "check in",
descrption: "daily check in rewards.",
async execute(message) {
let user = message.mentions.users.first() || message.author;
let daily = await db.fetch(`daily_${message.author.id}`);
let money = db.fetch(`money_${user.id}`);
let cooldown = 1000*60*60*20
let amount = Math.floor(Math.random() * 500) + 250
if (daily != null && cooldown - (Date.now() - daily) > 0) {
let time = ms(cooldown - (Date.now() - daily));
message.channel.send(`You have already collected the daily check in reward, please check in again in **${time.hours}h ${time.minutes}m ${time.seconds}s**`)
} else {
let embed = new Discord.MessageEmbed()
.setTitle('Daily Check In')
.setDescription(`Here is the amount collected today: ${amount}`)
.setColor('#ffc300')
message.channel.send({embeds: [embed]})
db.add(`money_${message.author.id}`, amount)
db.add(`daily_${message.author.id}`, Date.now())
}
}}
Cannot read property 'execute' of undefined"
Means client.commands.get('chk') is returning undefined.
Presumably, this means the chk command can't be found.
So, let's look at where you're setting the commands:
// fetch commands
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.name, command);
}
And let's check your chk.js file which is being imported.
module.exports = {
name: "check in",
// ...
What I can see is, you're exporting the module and setting the command with the name "check in" but later in the script, you're asking for a command called "chk". The command can't be found, returns undefined, and kills your code as it isn't handled.
The solution in this case is to just change your name property to "chk", or request client.commands.get("check in") instead.
I'm very very new to programming and I'm trying to set up a basic discord bot that sends a video every Friday. Currently I have:
Index.js which contains:
const Discord = require("discord.js");
const fs = require("fs");
require("dotenv").config()
const token = process.env.token;
const { prefix } = require("./config.js");
const client = new Discord.Client();
const commands = {};
// load commands
const commandFiles = fs.readdirSync("./commands").filter(file => file.endsWith(".js"));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
commands[command.name] = command;
}
// login bot
client.on("ready", () => {
console.log(`Logged in as ${client.user.tag}`);
});
client.on("message", message => {
if(!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const command = args.shift().toLowerCase();
let cmd = commands[command];
if(cmd)
cmd.execute(message, args)
});
client.login(token);
and a commands folder which contains beeame.js and that contains:
module.exports = {
name: "beeame",
description: "b",
execute: (message) => {
message.channel.send("It's Friday!", { files: ["./beeame.mp4"] });
}
}
I have heard about cron jobs and intervals but I'm not sure how to add these to the code I currently have.
Any help would be super appreciated!
Nate,
Here's some basics, to get you started. Your existing project you showed, sets up your bot to handle messages whenever they arrive. Everything there stays as is. You want to add a new section to deal with the timers.
First, here's a snippet of a utility file having to deal with Cron Jobs:
const CronJob = require('cron').CronJob;
const job = new CronJob('* * * * *', function() {
const d = new Date();
console.log('At each 1 Minute:', d);
});
job.start();
the thing to study and pay attention to is the ' * * * ' area. You will want to understand this, to set the timing correctly.
so replace the console logging with your messages, set your timing correctly and you should be good to go. The other thing to remember is that wherever your bot is running, the time zone might be different than where you (or others are)...so you might need to adjust for that if you have specific time of day needs.
Edit:
Based on subsequent questions....and note, I didn't set the time right. you need to really do more research to understand it.
const cron = require('cron').CronJob;
const sendMessageWeekly = new cron('* * * * *', async function() {
const guild = client.guilds.cache.get(server_id_number);
if (guild) {
const ch = guild.channels.cache.get(channel_id_number);
await ch.send({ content: 'This is friendly reminder it is Friday somewhere' })
.catch(err => {
console.error(err);
});
}
});
sendMessageWeekly.start();
I want to download videos one after the other in a series.
That is the first one should be completely downloaded before the second one starts & the second one should be completely downloaded before the third one starts & so on.
I have the following directory structure -
video-downloader
├── index.js
├── videos.js
├── package.json
package.json
{
"name": "video-downloader",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"download": "^7.1.0"
},
"scripts": {
"start": "node index"
}
}
video.js
const videos = [
{
url: 'https://video.com/lesson1.mp4',
name: 'Lesson 1',
},
{
url: 'https://video.com/lesson2.mp4',
name: 'Lesson 2',
},
.
.
.
{
url: 'https://video.com/lesson2.mp4',
name: 'Lesson 100',
}
]
index.js
const fs = require('fs')
const download = require('download')
const videos = require('./videos')
const OUTPUT_DIR = 'Downloads'
fs.mkdir(OUTPUT_DIR, () => {
main()
})
const main = () => {
videos.map((video, i) => {
console.log(`Downloaded file ${i + 1} of ${videos.length} (${video.name})`)
download(video.url).pipe(
fs.createWriteStream(`${OUTPUT_DIR}/${video.name}.mp4`),
)
})
}
This downloads videos chunk by chunk parallelly. All the videos are downloaded at once but none of them gets completed before the other one starts.
How do I download it serially?
I know I should use something like http://caolan.github.io/async/ but it needs a function signature & I have videos as an array so I'm not sure how to go about it.
You can use the await keyword on standard for loops, and things will process in order, and wait on each download before proceeding.
const fs = require('fs')
const download = require('download')
const videos = require('./videos')
const util = require('util')
const mkdirAsync = util.promisify(fs.mkdir)
const OUTPUT_DIR = 'Downloads'
const main = async () => {
await mkdirAsync(OUTPUT_DIR)
for (let i = 0; i < videos.length; i++) {
const video = videos[i]
const data = await download(video.url)
fs.writeFileSync(`${OUTPUT_DIR}/${video.name}.mp4`, data)
console.log(`Downloaded file ${i + 1} of ${videos.length} (${video.name})`)
}
}
main()
You can use .reduce with promises to resolve sequentially, as follows:
const fs = require('fs')
const sh = require('shelljs')
const download = require('download')
const videos = require('./videos')
const OUTPUT_DIR = 'Downloads'
sh.mkdir('-p', OUTPUT_DIR)
videos = videos.reduce((acc, item) => {
return acc.then(() => {
return new Promise((resolve) => {
// Here you are using it as a Duplex Stream, not a promise,
// therefore, you must check when the stream emits the 'end' event
// so you can proceed further
let stream = download(video.url)
.pipe(fs.createWriteStream(`${OUTPUT_DIR}/${video.name}.mp4`));
stream.on('end', () => {
console.log(`stream done ${item}`);
resolve(item);
})
})
});
}, Promise.resolve());
// 'videos' is now a promise
videos.then((lastPromise) => {
// using reduce will return the last evaluated item(promise)
// but reaching the last one means the promises before that have been resolved
console.log('all files were downloaded');
})
Try async await for this. First download and then write in Sync.
const fs = require('fs');
const sh = require('shelljs');
const download = require('download');
const videos = require('./videos');
const OUTPUT_DIR = 'Downloads';
sh.mkdir('-p', OUTPUT_DIR);
videos.forEach(async (video, i) => {
console.log(`Downloading ${video.name}. Fil${i + 1}/${videos.length} - `);
const data = await download(video.url);
fs.writeFileSync(`${OUTPUT_DIR}/${video.name}.mp4`, data);
});
I am trying to create an interactive CLI that can run serial commands. I have two files serialcomms.js and cli.js. Serialcomms.js contains the connection, handlers, and command functions. cli.js contains the commander information.
My issue is that I can only call the send command once because the listeners/handlers take over from the serialcomms file. What would be the best method to loop the cli program so I can call the send command over and over again, but still have the serial handlers running and output to stdout? Would I need to use a child process? Or recursion to have the cli call itself?
Example behavior I am expecting with an echo bot on the other end of the serial line.
Send hello
hello
Send Bye
Bye
Behavior I am experiencing
Send hello
hello
endless wait
Here is my serialcomms.js
const SerialPort = require('serialport');
const ReadLine = require('#serialport/parser-readline');
let portName = `/dev/pts/${process.argv[2]}` || '/dev/pts/6';
let baudRate = process.argv[3] || 115200;
let myPort = new SerialPort(portName, {baudRate: baudRate})
let parser = myPort.pipe(new ReadLine({ delimiter: '\n' }))
myPort.on('open', () => {
console.log(`port ${portName} open`)
})
parser.on('data', (data) => {
console.log(data)
})
myPort.on('close', () => {
console.log(`port ${portName} closed`)
})
myPort.on('error', (err) => {
console.error('port error: ' + err)
})
function send(data){
myPort.write(JSON.stringify(data)+'\n', function(err) {
if (err) {
return console.log('Error on write: ', err.message);
}
console.log(`${data} sent`);
});
}
module.exports = {
send
}
Here is my CLI.js file
const program = require('commander');
const {send} = require('./serialcomms');
program
.version('1.0.0')
.description('Serial Tester')
program
.command('send <msg>')
.alias('s')
.description('send a message over serial')
.action((msg)=>{
send(msg)
})
program.parse(process.argv)