Discord.js - Adding queue function to music system - javascript

Yes, I know there are a lot of threads on here regarding this topic. HOWEVER; I've found that in most cases, it is kind of dependant on what your music code looks like. Therefore, I decided to not mess too much around and potentially break my code and clutter it with stuff I won't need.
So here I go.
Below is the code I have in my play.js file. Most of it I got through a guide I found just to get me going then I tweaked it a bit to be more suitable for my use.
const discord = require('discord.js');
//Setting up constants
const ytdl = require("ytdl-core");
const ytSearch = require("yt-search");
const voiceChannel = msg.member.voice.channel;
// Check if user is in voiceChannel
if (!voiceChannel) return msg.channel.send(errorVoiceEmbed);
// Check if we have the correct permissions
const permissions = voiceChannel.permissionsFor(msg.client.user);
if (!permissions.has("CONNECT")) return msg.channel.send(permsEmbed);
if (!permissions.has("SPEAK")) return msg.channel.send(permsEmbed);
// Check if a second argument has been passed
if (!args.length) return msg.channel.send(playEmbed);
// Validating the passed URL
const validURL = (str) => {
var regex = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/;
if (!regex.test(str)){
return false;
} else {
return true;
}
}
// If we have a valid URL, load it and play the audio
if(validURL (args[0])){
const connection = await voiceChannel.join();
const stream = ytdl(args[0], {filter: 'audioonly'});
connection.play(stream, {seek: 0, volume: 1})
// Leave when done
.on('finish', () => {
voiceChannel.leave();
msg.channel.send(completedEmbed);
});
await msg.reply(playingEmbed);
return
}
const connection = await voiceChannel.join();
// If a user enters a search query instead of a link, Search YouTube and play a result.
const videoFinder = async (query) => {
const videoResult = await ytSearch(query);
return (videoResult.videos.length > 1) ? videoResult.videos[0] : null;
}
const video = await videoFinder(args.join(' '));
if (video) {
const stream = ytdl(video.url, {filter: 'audioonly'});
connection.play(stream, {seek: 0, volume: 1})
.on('finish', () => {
voiceChannel.leave();
msg.channel.send(completedEmbed);
});
await msg.reply (playingEmbed);
} else {
msg.channel.send(noResultsEmbed);
}
So - How would I go about adding a proper queue to this? I'm looking for a separate queue command that ties in perfectly with the play command. Therefore - I'm gonna be using two different files that would need to communicate through a songlist of some sort. How this would be done tho, I'm unsure. I looked into just using arrays for this but didn't manage to get that working.
And before you ask - Those embeds that are used in those msg.channel.send statements are embeds configured earlier up in the file. I didn't include these in here but they're there.
PLEASE NOTE:
I'm not looking for a complete solution. I just want some tips and hints as to how I can solve this in a simple and effective way without having to clutter up the code I already have.
That being said - Just on it's own, the code for the play command works perfectly . It plays either the requested song from the link provided or a song from the search query. But when a new song is requested, the old one just stops and the new one plays. You all know how this should go. If no song is playing, play it. If a song is played, add the requested song to the queue and play that once the previous song is done etc etc.

Related

Quick.db, SQLite database file wiping on Bot reboot. Discord.js

I'm trying to save a value of the server being "Down" or "Up", it seems to be working fine, however, when I clear the console, then run the bot again, it looks to have reset the database fully, is there a way to make the database, work like a normal database would? (Save after restart)
I'm having a check to see if the server is up, then don't allow it to run the command and say it's already online.
I wasn't like this before, and I haven't used quick.db for a while, so sorry if I missed anything.
Code:
// Main requirements
const { ROLE_SSUPIN, CHANNEL_SSU, CHANNEL_ROLESEL, BOT_PREFIX } = require('../../config.json')
const commandName = 'ssu'
// Optional Requirements
const { MessageEmbed } = require('discord.js')
const { QuickDB } = require('quick.db');
const db = new QuickDB();
// Main Code
module.exports = async client => {
client.on('messageCreate', async message => {
if (message.author.bot) return;
if (!message.content.toLowerCase().startsWith(BOT_PREFIX)) return;
const command = message.content.split(' ')
const args = message.content.substring(1).split(' ')
if (command.toString().toLowerCase().replace(BOT_PREFIX, '').startsWith(commandName)) {
const ssuStatus = await db.get("ssu.status")
await db.set("ssu.status", "down");
console.log(ssuStatus)
if (!message.member.roles.cache.get('998320362234323026')) return message.reply("Only staff with `SSU Permissions` can use this command!")//.then(m => {setTimeout( function() { m.delete(), message.delete() }, 10000)});
if (!message.channel.id == CHANNEL_SSU) return message.reply(`Please use this command in <#${CHANNEL_SSU}>.`)
if (ssuStatus == 'up') return message.reply(`Server Status is already set to Online. Say \`${BOT_PREFIX}Server Status Set Offline\` if you believe this is a mistake.`)
message.channel.send(`
Start up done.
`)
.then(await db.set("ssu.status", "up"))
}
})
}
I had "await db.set("ssu.status", "down");" in the start of each function, so it was reseting it back to "Down", I didn't mean for it do that, if you have the same issue, make sure that you don't have anything that sets the database value in the start, as it might be the reason it's getting reset and you think it's wiping.

Async function doesn't pop off a second time

I'm creating a button to record a canvas using FFMPEG. Here's the code that finalizes the download process.
const recordButton = document.querySelector("#record")
recordButton.addEventListener('click', function () {
function startRecording() {
const { createFFmpeg } = FFmpeg;
const ffmpeg = createFFmpeg({
log: true
});
var transcode = async (webcamData) => {
var name = `record${id}.webm`;
await ffmpeg.load();
await ffmpeg.write(name, webcamData);
await ffmpeg.transcode(name, `output${id}.mp4`);
var data = ffmpeg.read(`output${id}.mp4`);
var video = document.getElementById('output-video');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
dl.href = video.src;
}
fn().then(async ({ url, blob }) => {
transcode(new Uint8Array(await (blob).arrayBuffer()));
})
...
id += 1}
The problem arises with the transcode variable. While the button works initially, every other attempt (on a single page load) fails just the async function. I'm not well versed enough in the function to know why it would only work once. That said, I do know it is the only bit of code that does not fire off upon second attempt.
It could be a few things. This is borrowed code, and I've repurposed it for multiple uses. I may have messed up the declarations. It may be an async issue. I tried to use available values to rig up a secondary, similar function, but that would defeat the purpose of the first.
I tried clearing and appending the DOM elements affected, but that doesn't do anything.
It seems to have something to do with:
await ffmpeg.load();
While FFMPEG has to download and load the library initially, it does not have to do so the second initialization. That my be the trigger that is not activating with successive uses.

discord.js - music bot is buggy

After around 1-2 minutes playin a song my bot says that the playing be finished whatever the songs length is. here is the link to it: https://github.com/Sheesher/amos
i guess this bug aint be caused due to the code...
const ytdl = require('ytdl-core-discord');
const validUrl = require('valid-url');
let servers = {};
const play = (msg) => {
const args = msg.content.substring(1).split(" ");
const link = args[1];
if (!link) {
return msg.channel.send('You must provide a link.');
};
if (!msg.member.voice.channel) {
return msg.channel.send('You have to be in a voice chat.');
}
if (!validUrl.isUri(link)) {
return msg.channel.send('That aint be link.');
}
if (!servers[msg.guild.id]) servers[msg.guild.id] = {
queque: []
}
let server = servers[msg.guild.id];
server.queque.push(link);
if (!msg.guild.voiceConnection) msg.member.voice.channel.join().then((connection) => {
playSong(connection, link, msg);
})
}
const playSong = async (connection, url, msg) => {
const server = servers[msg.guild.id];
server.dispatcher = connection.play(await ytdl(url), { type: 'opus' });
server.queque.shift();
server.dispatcher.on("end", () => {
if (server.queque[0]) {
playSong(connection, url);
} else {
connection.disconnect();
}
})
server.dispatcher.on('finish', () => log(s('playing finished')))
}```
This is actually any issue many others suffer from as well. It's because of how YTDL-Core (even the discord version) is handled with the streams on YouTube. If it loses connection then it tries to redirect too it, redirect too much and crash or skips the song. Even the music bot I recently created suffer from this but kept it that way for beginners to learn from it. The way to do this is honestly to just not use YTDL-Core. Use something like lavalink which handles all the music playing for you.

WebRTC both remote and local video is displayed with local stream

hello i am newbie in WebRTC and i tried this code
const yourVideo = document.querySelector("#face_cam_vid");
const theirVideo = document.querySelector("#thevid");
(async () => {
if (!("mediaDevices" in navigator) || !("RTCPeerConnection" in window)) {
alert("Sorry, your browser does not support WebRTC.");
return;
}
const stream = await navigator.mediaDevices.getUserMedia({video: true,
audio: true});
yourVideo.srcObject = stream;
const configuration = {
iceServers: [{urls: "stun:stun.1.google.com:19302"}]
};
const yours = new RTCPeerConnection(configuration);
const theirs = new RTCPeerConnection(configuration);
for (const track of stream.getTracks()) {
yours.addTrack(track, stream);
}
theirs.ontrack = e => theirVideo.srcObject = e.streams[0];
yours.onicecandidate = e => theirs.addIceCandidate(e.candidate);
theirs.onicecandidate = e => yours.addIceCandidate(e.candidate);
const offer = await yours.createOffer();
await yours.setLocalDescription(offer);
await theirs.setRemoteDescription(offer);
const answer = await theirs.createAnswer();
await theirs.setLocalDescription(answer);
await yours.setRemoteDescription(answer);
})();
and it works but partly https://imgur.com/a/nG7Xif6 . in short i am creating ONE-to-ONE random video chatting like in omegle but this code displays both "remote"(stranger's) and "local"("mine") video with my local stream but all i want is , user wait for second user to have video chat and when third user enters it should wait for fourth and etc. i hope someone will help me with that
You're confusing a local-loop demo—what you have—with a chat room.
A local-loop demo is a short-circuit client-only proof-of-concept, linking two peer connections on the same page to each other. Utterly useless, except to prove the API works and the browser can talk to itself.
It contains localPeerConnection and remotePeerConnection—or pc1 and pc2—and is not how one would typically write WebRTC code. It leaves out signaling.
Signaling is hard to demo without a server, but I show people this tab demo. Right-click and open it in two adjacent windows, and click the Call! button on one to call the other. It uses localSocket, a non-production hack I made using localStorage for signaling.
Just as useless, a tab-demo looks more like real code:
const pc = new RTCPeerConnection();
call.onclick = async () => {
video.srcObject = await navigator.mediaDevices.getUserMedia({video:true, audio:true});
for (const track of video.srcObject.getTracks()) {
pc.addTrack(track, video.srcObject);
}
};
pc.ontrack = e => video.srcObject = e.streams[0];
pc.oniceconnectionstatechange = e => console.log(pc.iceConnectionState);
pc.onicecandidate = ({candidate}) => sc.send({candidate});
pc.onnegotiationneeded = async e => {
await pc.setLocalDescription(await pc.createOffer());
sc.send({sdp: pc.localDescription});
}
const sc = new localSocket();
sc.onmessage = async ({data: {sdp, candidate}}) => {
if (sdp) {
await pc.setRemoteDescription(sdp);
if (sdp.type == "offer") {
await pc.setLocalDescription(await pc.createAnswer());
sc.send({sdp: pc.localDescription});
}
} else if (candidate) await pc.addIceCandidate(candidate);
}
There's a single pc—your half of the call—and there's an onmessage signaling callback to handle the timing-critical asymmetric offer/answer negotiation correctly. Same JS on both sides.
This still isn't a chat-room. For that you need server logic to determine how people meet, and a web socket server for signaling. Try this tutorial on MDN which culminates in a chat demo.

Streaming data from Firebase Realtime Database

I have over 20k documents in my Realtime Database. I need to stream them, but I'm not even sure how to get started with it. This is kind of what I was trying to go for
sendEmail.get('/:types/:message', cors(), async (req, res, next) => {
console.log(5);
const types = JSON.parse(req.params.types);
console.log('types', types);
let recipients = [];
let mails = [];
if (types.includes('students')) {
console.log(1);
const tmpUsers = await admin.database().ref('Users').orderByChild('student').equalTo(true).once('value').then(r => r.val()).catch(e => console.log(e));
recipients = recipients.concat(tmpUsers);
}
if (types.includes('solvers')) {
console.log(2);
let tmpUsers = await admin.database().ref('Users').orderByChild('userType').equalTo('person').once('value').then(r => r.val()).catch(e => console.log(e));
tmpUsers = tmpUsers.concat(arrayFromObject(await admin.database().ref('Users').orderByChild('userType').equalTo('company').once('value').then(r => r.val()).catch(e => console.log(e))));
recipients = recipients.concat(tmpUsers);
}
});
But this code causes my server to run out of memory. Someone suggested streams in my previous question, but as much as I like the idea, I have no idea how to actually do the streaming. I know it should be something like:
const fs = require('fs');
const readStream = fs.createReadStream('path goes here');
const data = [];
readStream.on('data', chunk => {
data.push(chunk);
})
readStream.on('end', () => {
console.log(data);
res.end(data);
});
But how on earth do I pass a firebase query into the path?
I tried this, but it said Argument type Promise is not assignable to parameter type PathLike, which makes sense, but how do I get around it?
const users = fs.createReadStream(admin.database().ref('News').once('value').then(r => r.val()))
To summarize: How do I stream data from a Firebase realtime database?
Edit:
How is that a duplicate? These are 2 completely different questions. The starting code is the same but the ways of solving I'm asking about are completely different
Realtime Database doesn't have any streaming APIs. When you perform a query, the entire result set is always loaded into memory in one shot.
A possible alternative is to page through the results using limitToFirst() along with some query ordering that allows you to perform a subsequent query after the first batch of items is processed, picking up where the last query left off.

Categories