How to make the discord bot queue local mp3? - python-3.x

I'm new to python, so I wonder if there's a way to do so.
Here's my play mp3 command:
#bot.command()
async def play_song1(ctx):
global voice
channel = ctx.message.author.voice.channel
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.move_to(channel)
else:
voice = await channel.connect()
voice.play(discord.FFmpegPCMAudio('./mp3/song1.mp3'))
voice.source = discord.PCMVolumeTransformer(voice.source)
voice.source.volume = 0.1
await ctx.send ('playing')
while voice.is_playing():
await asyncio.sleep(.1)
await voice.disconnect()
I made 2 more same commands but for song2 and song3, and now I want to queue the mp3 when someone calls them.

Seen as if you're not using cogs, you could try something like this:
guild_queues = {} # Multi-server support as a dict, just use a list for one server
# EDIT: Map song names in a dict
play_messages = {
"song1": "Now playing something cool!",
"song2": "And something even cooler just started playing!",
"song3": "THE COOLEST!"
}
async def handle_queue(ctx, song):
voice = discord.utils.get(bot.voice_clients, guild=ctx.guild)
channel = ctx.author.voice.channel
if voice and channel and voice.channel != channel:
await voice.move_to(channel)
elif not voice and channel:
voice = await channel.connect()
if not voice.is_playing():
audio = discord.FFmpegPCMAudio(f"./{song}.mp3")
source = discord.PCMVolumeTransformer(audio)
source.volume = 0.1
voice.play(audio)
await ctx.send(play_messages[song])
while voice.is_playing():
await asyncio.sleep(.1)
if len(guild_queues[ctx.guild.id]) > 0:
next_song = guild_queues[ctx.guild.id].pop(0)
await handle_queue(ctx, next_song)
else:
await voice.disconnect()
#bot.command()
async def play(ctx, *, song): # I'd recommend adding the filenames as an arg, but it's up to you
# Feel free to add a check if the filename exists
try:
# Get the current guild's queue
queue = guild_queues[ctx.guild.id]
except KeyError:
# Create a queue if it doesn't already exist
guild_queues[ctx.guild.id] = []
queue = guild_queues[ctx.guild.id]
voice = discord.utils.get(bot.voice_clients, guild=ctx.guild)
queue.append(song)
# The one song would be the one currently playing
if voice and len(queue) > 0:
await ctx.send("Added to queue!")
else:
current_song = queue.pop(0)
await handle_queue(ctx, current_song)
References:
utils.get()
Client.voice_clients
Context.guild
Member.voice
VoiceState.channel
VoiceClient.move_to()
VoiceClient.is_playing()
VoiceClient.play()
discord.FFmpegPCMAudio()
discord.PCMVolumeTransformer()
Context.send()

Related

Discord Bot not playing audio when visually it is

I am making a Discord Bot that plays music when I give it the name of a song.
Whenever I give it the ".play" command, the Discord app shows that the bot is in fact playing audio, I just don't hear any.
I tried multiple times with different music, however I recieved the same problem.
Here is my code:
import discord
from discord.ext import commands
import wavelink
client = commands.Bot(command_prefix = ".", intents = discord.Intents.all())
class CustomPlayer (wavelink.Player):
def __init__(self):
super().__init__()
self.queue = wavelink.Queue()
#client.event
async def on_ready():
client.loop.create_task(connect_nodes()) #HTTPS and Websocket operations
async def connect_nodes(): #Helper function
await client.wait_until_ready()
await wavelink.NodePool.create_node(
bot = client ,
host = "127.0.0.1" ,
port = 2333 ,
password = "youshallnotpass"
)
#client.event
async def on_wavelink_node_ready(node = wavelink.Node):
print(f"Node: <{node.identifier}> is ready!")
#client.command()
async def connect(ctx):
vc = ctx.voice_client
try:
channel = ctx.author.voice.channel
except AttributeError:
return await ctx.send("Please join a channel to connect.")
if not vc:
await ctx.author.voice.channel.connect(cls = CustomPlayer())
else:
await ctx.send("The bot is already connected to a voice channel.")
#client.command()
async def disconnect(ctx):
vc = ctx.voice_client
if vc:
await vc.disconnect()
else:
await ctx.send("Bot is not connected to a voice channel.")
#client.command()
async def play(ctx, *, search: wavelink.YouTubeTrack):
vc = ctx.voice_client #represents a discord voice connection
if not vc:
custom_player = CustomPlayer()
vc: CustomPlayer = await ctx.author.voice.channel.connect(cls = custom_player)
if vc.is_playing():
vc.queue.put(item = search)
await ctx.send(embed = discord.Embed(
title = search.title,
url = search.uri,
description = f"Queued {search.title} in {vc.channel}"
))
else:
await vc.play(search)
await ctx.send(embed = discord.Embed(
title = search.title,
url = search.uri,
description = f"Playing {vc.source.title} in {vc.channel}"))

How do I stream and queue up songs in a discord bot?

What I want to do?
Search a music by its name
Stream the music instead of downloading and playing it
Queue up songs if one song is already playing
Output to the author "Playing [url]"
My code:
async def play(ctx, url : str):
song_there = os.path.isfile("song.mp3")
try:
if song_there:
os.remove("song.mp3")
except PermissionError:
await ctx.send("Wait for the current playing music to end or use the 'stop' command")
return
channel = ctx.author.voice.channel
'''voiceChannel = discord.utils.get(ctx.guild.voice_channels, name=channel.name)'''
voice_client = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice_client == None:
await channel.connect()
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
ydl_opts = {
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
}
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
for file in os.listdir("./"):
if file.endswith(".mp3"):
os.rename(file, "song.mp3")
voice.play(discord.FFmpegPCMAudio("song.mp3"))
This code is very basic. It uses yt-dl and works only if you paste a url of the YouTube video.
It downloads the song and plays it which takes time for the bot to play the music
It cannot queue up songs.
It is my first time making a bot and I'm not really experienced in reading and understanding the docs as I'm just getting started in making python projects.
this is how here at Rage bots we make our music commands and its very usefull
import discord
from discord.ext import commands
from random import choice
import string
from discord.ext.commands.cooldowns import BucketType
import asyncio
import youtube_dl
import pafy
import datetime
from discord_slash import cog_ext, SlashContext
x = datetime.datetime.now()
from ult import *
class music(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.song_queue = {}
self.setup()
def setup(self):
for guild in self.bot.guilds:
self.song_queue[guild.id] = []
async def check_queue(self, ctx):
if len(self.song_queue[ctx.guild.id]) > 0:
ctx.voice_client.stop()
await self.play_song(ctx, self.song_queue[ctx.guild.id][0])
self.song_queue[ctx.guild.id].pop(0)
async def search_song(self, amount, song, get_url=False):
info = await self.bot.loop.run_in_executor(None, lambda: youtube_dl.YoutubeDL({"format" : "bestaudio", "quiet" : True}).extract_info(f"ytsearch{amount}:{song}", download=False, ie_key="YoutubeSearch"))
if len(info["entries"]) == 0: return None
return [entry["webpage_url"] for entry in info["entries"]] if get_url else info
async def play_song(self, ctx, song):
url = pafy.new(song).getbestaudio().url
ctx.voice_client.play(discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(url)), after=lambda error: self.bot.loop.create_task(self.check_queue(ctx)))
ctx.voice_client.source.volume = 0.5
#commands.command()
async def pause(self, ctx):
if await get_mute(ctx.author) != 0:
await ctx.send("You are blacklisted from the bot")
else:
ctx.voice_client.pause()
await ctx.send("*Paused -* ⏸️")
#commands.command()
async def resume(self, ctx):
if await get_mute(ctx.author) != 0:
await ctx.send("You are blacklisted from the bot")
else:
ctx.voice_client.resume()
await ctx.send("*Resuming -* ▶️")
#commands.command()
#commands.is_owner()
#commands.cooldown(1, 5, commands.BucketType.user)
async def oskip(self, ctx):
commandd = "oskip"
print(f"{ctx.author.name}, {ctx.author.id} used command "+commandd+" used at ")
print(x)
print(" ")
await ctx.send("skipping")
ctx.voice_client.stop()
await self.check_queue(ctx)
#commands.command()
#commands.cooldown(1, 5, commands.BucketType.user)
async def join(self, ctx):
if await get_mute(ctx.author) != 0:
await ctx.send("You are blacklisted from the bot")
else:
commandd = "join"
print(f"{ctx.author.name}, {ctx.author.id} used command "+commandd+" used at ")
print(x)
print(" ")
if ctx.author.voice is None:
return await ctx.send("You are not connected to a voice channel, please connect to the channel you want the bot to join.")
if ctx.voice_client is not None:
await ctx.voice_client.disconnect()
await ctx.author.voice.channel.connect()
#commands.command()
#commands.cooldown(1, 5, commands.BucketType.user)
async def leave(self, ctx):
if await get_mute(ctx.author) != 0:
await ctx.send("You are blacklisted from the bot")
else:
commandd = "leave"
print(f"{ctx.author.name}, {ctx.author.id} used command "+commandd+" used at ")
print(x)
print(" ")
if ctx.voice_client is not None:
return await ctx.voice_client.disconnect()
await ctx.send("I am not connected to a voice channel.")
#commands.command()
async def play(self, ctx, *, song=None):
if await get_mute(ctx.author) != 0:
await ctx.send("You are blacklisted from the bot")
else:
commandd = "play"
print(f"{ctx.author.name}, {ctx.author.id} used command "+commandd+" used at ")
print(x)
print(" ")
if song is None:
return await ctx.send("You must include a song to play.")
if ctx.voice_client is None:
return await ctx.send("I must be in a voice channel to play a song.")
# handle song where song isn't url
if not ("youtube.com/watch?" in song or "https://youtu.be/" in song):
await ctx.send("Searching for song, this may take a few seconds.")
result = await self.search_song(1, song, get_url=True)
if result is None:
return await ctx.send("Sorry, I could not find the given song, try using my search command.")
song = result[0]
if ctx.voice_client.source is not None:
queue_len = len(self.song_queue[ctx.guild.id])
if queue_len < 10:
self.song_queue[ctx.guild.id].append(song)
return await ctx.send(f"I am currently playing a song, this song has been added to the queue at position: {queue_len+1}.")
else:
return await ctx.send("Sorry, I can only queue up to 10 songs, please wait for the current song to finish.")
await self.play_song(ctx, song)
await ctx.send(f"Now playing: {song}")
#commands.command()
#commands.cooldown(1, 5, commands.BucketType.user)
async def search(self, ctx, *, song=None):
if await get_mute(ctx.author) != 0:
await ctx.send("You are blacklisted from the bot")
else:
commandd = "search"
print(f"{ctx.author.name}, {ctx.author.id} used command "+commandd+" used at ")
print(x)
print(" ")
if song is None: return await ctx.send("You forgot to include a song to search for.")
await ctx.send("Searching for song, this may take a few seconds.")
info = await self.search_song(5, song)
embed = discord.Embed(title=f"Results for '{song}':", description="*You can use these URL's to play an exact song if the one you want isn't the first result.*\n", colour=discord.Color.green())
amount = 0
for entry in info["entries"]:
embed.description += f"[{entry['title']}]({entry['webpage_url']})\n"
amount += 1
embed.set_footer(text=f"Displaying the first {amount} results.")
await ctx.send(embed=embed)
#commands.command()
#commands.cooldown(1, 5, commands.BucketType.user)
async def queue(self, ctx):
if await get_mute(ctx.author) != 0:
await ctx.send("You are blacklisted from the bot")
else: # display the current guilds queue
commandd = "queue"
print(f"{ctx.author.name}, {ctx.author.id} used command "+commandd+" used at ")
print(x)
print(" ")
if len(self.song_queue[ctx.guild.id]) == 0:
return await ctx.send("There are currently no songs in the queue.")
embed = discord.Embed(title="Song Queue", description="", colour=discord.Color.green().dark_gold())
i = 1
for url in self.song_queue[ctx.guild.id]:
embed.description += f"{i}) {url}\n"
i += 1
embed.set_footer(text="Thanks for using me!")
await ctx.send(embed=embed)
#commands.command()
#commands.has_permissions(administrator=True)
#commands.cooldown(1, 5, commands.BucketType.user)
async def adskip(self, ctx):
if await get_mute(ctx.author) != 0:
await ctx.send("You are blacklisted from the bot")
else:
commandd = "adskip"
print(f"{ctx.author.name}, {ctx.author.id} used command "+commandd+" used at ")
print(x)
print(" ")
await ctx.send("skipping")
ctx.voice_client.stop()
await self.check_queue(ctx)
#commands.command()
#commands.cooldown(1, 5, commands.BucketType.user)
async def skip(self, ctx):
if await get_mute(ctx.author) != 0:
await ctx.send("You are blacklisted from the bot")
else:
commandd = "skip"
print(f"{ctx.author.name}, {ctx.author.id} used command "+commandd+" used at ")
print(x)
print(" ")
if ctx.voice_client is None:
return await ctx.send("I am not playing any song.")
if ctx.author.voice is None:
return await ctx.send("You are not connected to any voice channel.")
if ctx.author.voice.channel.id != ctx.voice_client.channel.id:
return await ctx.send("I am not currently playing any songs for you.")
poll = discord.Embed(title=f"Vote to Skip Song by - {ctx.author.name}#{ctx.author.discriminator}", description="**80% of the voice channel must vote to skip for it to pass.**", colour=discord.Color.blue())
poll.add_field(name="Skip", value=":white_check_mark:")
poll.add_field(name="Stay", value=":no_entry_sign:")
poll.set_footer(text="Voting ends in 15 seconds.")
poll_msg = await ctx.send(embed=poll) # only returns temporary message, we need to get the cached message to get the reactions
poll_id = poll_msg.id
await poll_msg.add_reaction(u"\u2705") # yes
await poll_msg.add_reaction(u"\U0001F6AB") # no
await asyncio.sleep(15) # 15 seconds to vote
poll_msg = await ctx.channel.fetch_message(poll_id)
votes = {u"\u2705": 0, u"\U0001F6AB": 0}
reacted = []
for reaction in poll_msg.reactions:
if reaction.emoji in [u"\u2705", u"\U0001F6AB"]:
async for user in reaction.users():
if user.voice.channel.id == ctx.voice_client.channel.id and user.id not in reacted and not user.bot:
votes[reaction.emoji] += 1
reacted.append(user.id)
skip = False
if votes[u"\u2705"] > 0:
if votes[u"\U0001F6AB"] == 0 or votes[u"\u2705"] / (votes[u"\u2705"] + votes[u"\U0001F6AB"]) > 0.79: # 80% or higher
skip = True
embed = discord.Embed(title="Skip Successful", description="***Voting to skip the current song was succesful, skipping now.***", colour=discord.Color.green())
if not skip:
embed = discord.Embed(title="Skip Failed", description="*Voting to skip the current song has failed.*\n\n**Voting failed, the vote requires at least 80% of the members to skip.**", colour=discord.Color.red())
embed.set_footer(text="Voting has ended.")
await poll_msg.clear_reactions()
await poll_msg.edit(embed=embed)
if skip:
ctx.voice_client.stop()
await self.check_queue(ctx)
def setup(bot):
bot.add_cog(music(bot))
just make sure to get rid of the blacklist part
this has some fun stuff to that you didnt ask for but might be use full
if you need help setting it up add me on discord
RageWire#0001
-from Rage bot head dev and support team

discord.py stop command work but doesn't clear the queue

-----Play Section this works well-----
async def play(self, ctx, *, arg):
await ctx.channel.purge(limit=1)
try: channel = ctx.author.voice.channel
except: await ctx.send("❌ You're not connected to any channel!", delete_after = 5.0)
else:
channel = ctx.author.voice.channel
voice = get(self.bot.voice_clients, guild=ctx.guild)
song = Music.search(ctx.author.mention, arg)
------queue section------
if voice and voice.is_connected():
await voice.move_to(channel)
else:
voice = await channel.connect()
if not voice.is_playing():
self.song_queue[ctx.guild] = [song]
self.message[ctx.guild] = await ctx.send(embed=song['embed'])
voice.play(discord.FFmpegPCMAudio(song['source'], **Music.FFMPEG_OPTIONS), after=lambda e: self.play_next(ctx))
voice.is_playing()
else:
self.song_queue[ctx.guild].append(song)
await self.edit_message(ctx)
----stop section----
only skips music and don't clear queue
i don't know what is wrong here
#commands.command(brief='$stop')
async def stop(self, ctx):
voice = get(self.bot.voice_clients, guild=ctx.guild)
channel = ctx.message.author.voice.channel
await ctx.channel.purge(limit=1)
if voice and voice.is_playing():
await ctx.send('⛔ Music Stopped', delete_after = 5.0)
voice.stop(ctx.guild)
else:
await ctx.send("❌ I'm not playing any songs!", delete_after = 5.0)

How to Make a Discord Music Bot Using Discord.py Cogs

My Current Project
I'm trying to convert a Python music bot I made off a YouTube video into a cog, so my main Python Discord bot file isn't so cluttered.
My Problem
The code below I wrote isn't working. I read the docs on cogs, but I can't seem to find what I did wrong.
Here's the code I tried making, but it
import discord
from discord.ext import commands
from discord.utils import get
import youtube_dl
import os
from time import sleep
rpgmusicpath = r"path\to\music.mp3"
class Music(commands.Cog):
def __init__(self, client):
self.bot = client
#commands.Cog.listener()
async def on_ready(self):
print('Music cog successfully loaded.')
#commands.command(pass_context=True)
async def rpgmusic(ctx, self):
global voice
channel = ctx.message.author.voice.channel
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.move_to(channel)
else:
voice = await channel.connect()
print(f'Bot connected to voice channel {channel}\n')
await ctx.send(f'Playing some RPG music in {channel}.')
sleep(3)
voice.play(discord.FFmpegPCMAudio('rpgmusic.mp3'), after=lambda e: print(f'RPG music in {channel} has finished playing.'))
voice.source = discord.PCMVolumeTransformer(voice.source)
voice.source.volume = 0.05
#commands.command(pass_context=True)
async def join(ctx, self):
global voice
channel = ctx.message.author.voice.channel
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.move_to(channel)
else:
voice = await channel.connect()
print(f'Bot connected to voice channel {channel}\n')
await ctx.send(f'I joined {channel}.')
#commands.command(pass_context=True)
async def leave(ctx, self):
channel = ctx.message.author.voice.channel
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.disconnect()
print(f'Bot disconnected from channel {channel}.')
else:
print('Not able to disconnect to a voice channel because bot wasn\'t in one.')
#commands.command(pass_context=True)
async def play(ctx, url: str, self):
song_there = os.path.isfile('song.mp3')
try:
if song_there:
os.remove('song.mp3')
print('Removed current song.')
except PermissionError:
print('Error in deleting song file. (Song in use.)')
await ctx.send('Unable to request song. (Song already in use.)')
return
await ctx.send('Preparing song. Please wait.')
voice = get(bot.voice_clients, guild=ctx.guild)
ydl_opts = {
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
}
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
print('Downloading audio now.\n')
ydl.download([url])
for file in os.listdir('./'):
if file.endswith('.mp3'):
name = file
print(f'Renamed File: {file}.')
os.rename(file, 'song.mp3')
voice.play(discord.FFmpegPCMAudio('song.mp3'), after=lambda e: print(f'{name} has finished playing.'))
voice.source = discord.PCMVolumeTransformer(voice.source)
voice.source.volume = 0.06
nname = name.rsplit('-', 2)
await ctx.send(f'Now playing {name}.')
print('Now playing.\n')
def setup(bot):
bot.add_cog(Music(bot))
Two problems :
You didn't replaced bot by self.bot.
In rpgmusic, join, leave and play commands, change :
voice = get(bot.voice_clients, guild=ctx.guild)
To :
voice = get(self.bot.voice_clients, guild=ctx.guild)
Your first argument must be self, and not ctx:
#commands.command(pass_context=True)
async def rpgmusic(self, ctx)
#commands.command(pass_context=True)
async def join(self, ctx)
#commands.command(pass_context=True)
async def play(self, ctx)
Also, since you have a join function, you can await it in rpgmusic (you also don't need global voice):
#commands.command(pass_context=True)
async def rpgmusic(ctx, self):
await self.join(ctx)
await ctx.send(f'Playing some RPG music in {channel}.')
sleep(3)
voice.play(discord.FFmpegPCMAudio('rpgmusic.mp3'), after=lambda e: print(f'RPG music in {channel} has finished playing.'))
voice.source = discord.PCMVolumeTransformer(voice.source)
voice.source.volume = 0.05

How to get audio clip to play entirely before disconnecting

I have the bot joining and playing the audio but it doesn't seem to be consistent all the time. Sometimes it joins and plays starting halfway through. I'm assuming this has to do with the nature of async. Is there a way to ensure that the bot joins plays the audio then leaves in that order? My code is below
#bot.command(name='play_audio')
async def play_audio(ctx):
if ctx.message.author == client.user:
return
voice = await join(ctx)
voice.play(discord.FFmpegPCMAudio(f"{os.path.dirname(os.path.abspath(__file__))}/audio.mp3"))
await leave(ctx)
#bot.command(name='j')
async def join(ctx):
if ctx.message.author == client.user:
return
channel = ctx.message.author.voice.channel
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.move_to(channel)
else:
voice = await channel.connect()
print(f"The bot has connected to {channel}\n")
return voice
#bot.command(name='l')
async def leave(ctx):
if ctx.message.author == client.user:
return
channel = ctx.message.author.voice.channel
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.disconnect()
print(f"The bot has left {channel}\n")
bot.run(TOKEN)
It appears that you are executing leave() before the playing is completed.
You can check is_playing() to wait for the mp3 to complete.
Also, using asyncio.sleep to chill the while loop.
Try adding the following while before you leave:
#bot.command(name='play_audio')
async def play_audio(ctx):
if ctx.message.author == client.user:
return
voice = await join(ctx)
voice.play(discord.FFmpegPCMAudio(f"{os.path.dirname(os.path.abspath(__file__))}/audio.mp3"))
while voice.is_playing():
await sleep(1)
await leave(ctx)

Resources