Search code examples
pythonffmpegdiscorddiscord.pyyoutube-dl

discord bot music with youtube dl not stuck at webpage downloading


So I'm trying to make a music bot which just joins,plays,stops and resume music. However my bot can join and leave a voice channel fine, however when i do my /play command it get stuck on [youtube] oCveByMXd_0: Downloading webpage (this is output in vscode) and then does nothing after. I put some print statement (which you can see in the code below and it prints 1 and 2 but NOT 3). Anyone had this issue?

MUSIC BOT FILE

import discord
from discord.ext import commands
import youtube_dl

class music(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        
    @commands.command()
    async def join(self, ctx):
        if(ctx.author.voice is None):
            await ctx.reply("*You're not in a voice channel.*")
        voiceChannel = ctx.author.voice.channel
        if(ctx.voice_client is None): # if bot is not in voice channel
            await voiceChannel.connect()
        else: # bot is in voice channel move it to new one
            await ctx.voice_client.move_to(voiceChannel)

    @commands.command()
    async def leave(self, ctx):
        await ctx.voice_client.disconnect()
        
    @commands.command()
    async def play(self,ctx, url:str = None):
        if(url == None):
            await ctx.reply("*Check your arguments!*\n```/play VIDEO_URL```")
        else:
            ctx.voice_client.stop() # stop current song
            
            # FFMPEG handle streaming in discord, and has some standard options we need to include
            FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
            YTDL_OPTIONS = {"format":"bestaudio"}
            vc = ctx.voice_client
            
            # Create stream to play audio and then stream directly into vc
            with youtube_dl.YoutubeDL(YTDL_OPTIONS) as ydl:
                info = ydl.extract_info(url, download=False)
                print("1")
                url2 = info["formats"][0]["url"]
                print("2")
                source = await discord.FFmpegOpusAudio.from_probe(url2,FFMPEG_OPTIONS)
                print("3")
                vc.play(source) # play the audio
                await ctx.send(f"*Playing {info['title']} -* 🎵")
    
    @commands.command()
    async def pause(self, ctx):
        await ctx.voice_client.pause()
        await ctx.reply("*Paused -* ⏸️")
    
    @commands.command()
    async def resume(self, ctx):
        await ctx.voice_client.resume()
        await ctx.reply("*Resuming -* ▶️")
      
            
def setup(bot):
    bot.add_cog(music(bot))

MAIN FILE

from discord.ext import commands
from dotenv import load_dotenv
from lxml import html
import youtube_dl
import requests
import random
import discord
import requests
import os
import music

# Load .env file
load_dotenv()

COGS = [music]
PREFIX = "/"

bot = commands.Bot(command_prefix=PREFIX, intents=discord.Intents.all())

for x in range(len(COGS)):
    COGS[x].setup(bot)
    

# EVENTS #
@bot.event
async def on_ready():
    await bot.get_channel(888736019590053898).send(f"We back online! All thanks to *sploosh* :D")

@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.CommandNotFound):
        replies = ["Err is that even a command?", "Can you type bro?", "Yeah... thats not a command buddy.", "Sorry forgot you can't spell"]
        await ctx.send(random.choice(replies))
        
@bot.event
async def on_message(message):
    if str(message.channel) == "images-videos" and message.content != "":
        await message.delete()
    await bot.process_commands(message)
    

# COMMANDS #
@bot.command()
async def hello(ctx):
    # Get a random fact
    url = 'http://randomfactgenerator.net/'
    page = requests.get(url)
    tree = html.fromstring(page.content)
    hr = str(tree.xpath('/html/body/div/div[4]/div[2]/text()'))
    
    await ctx.reply("**Hello Bozo!**\n" + "*Random Fact : *" + hr[:-9]+"]")
    
@bot.command()
async def randomNum(ctx, start:int = None, end:int = None):
    if(start == None or end == None):
        await ctx.reply("*Check your arguments!*\n```/randomNum START_NUMBER END_NUMBER```")
    else:
        randNum = random.randint(start, end)
        await ctx.reply(f"*{randNum}*")

@bot.command()
@commands.is_owner()
async def kick(ctx, member:discord.Member = None, *, reason="You smell bozo."):
    if(member == None):
        await ctx.reply("*Check your arguments!*\n```/kick @MEMBER REASON(optional)```")
    elif ctx.author.id in (member.id, bot.user.id):
        await ctx.reply("*You cant kick yourself/me you silly.*")
    else:
        await member.kick(reason=reason)

@bot.command()
@commands.is_owner()
async def ban(ctx, member:discord.Member = None, *, reason="Bye Bye! :D."):
    if(member == None):
        await ctx.reply("*Check your arguments!*\n```/kick @MEMBER REASON(optional)```")
    elif ctx.author.id in (member.id, bot.user.id):
        await ctx.reply("*You cant ban yourself/me you silly.*")
    else:
        await member.ban(reason=reason)
        
@bot.command()
@commands.is_owner()
async def close_bot(ctx):
    replies = ["Well bye!", "Guess I go now?", "Please let me stay..."]
    await ctx.send(random.choice(replies))
    await bot.close()

    
if __name__ == "__main__":
    bot.run(os.getenv("BOT_TOKEN"))
    

Solution

  • This is how my play music command is setup, and i know for sure it works and it seems like it should work for you too.

    @commands.command()
        async def play(self, ctx, *, song=None):
                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}")
    

    this is some other things that you might need

    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