How to make a telegram bot that converts simple youtube link (something like https://www.youtube.com/watch?v=v3F3v8yNucc) into .mp3?
Please suggest something with minimum efford
This will do the minimum job:
import os
import telebot
import youtube_dl
# get YOUR_API_KEY from #BotFather
bot = telebot.TeleBot("YOUR_API_KEY")
#bot.message_handler(commands=['start'])
def shoot(message):
bot.send_message(message.chat.id, "Gimme YouTube link")
#bot.message_handler()
def run(message):
if "https://www.youtube.com" not in message.text:
print("This is not YouTube link!")
return
bot.send_message(message.chat.id, "Please wait...")
video_info = youtube_dl.YoutubeDL().extract_info(
url = message.text, download=False
)
filename = f"{video_info['title']}.mp3"
options={
'format':'bestaudio/best',
'keepvideo':False,
'outtmpl':filename,
}
with youtube_dl.YoutubeDL(options) as ydl:
ydl.download([video_info['webpage_url']])
print("Download complete... {}".format(filename))
bot.send_audio(message.chat.id, audio=open(filename, 'rb'))
bot.polling()
Thise code will download mp3 music to your server and sends it to the user in telegram, so you might wanna delete it after you send it, or need a larger server? Up to you.
I'm creating a discord bot which also has a music part. I have it working but i wanted to give the info to the user of which song it's playing. Right now I've this for the play command:
#commands.command()
async def play(self,ctx,url):
await ctx.channel.purge(limit = 1)
ctx.voice_client.stop()
FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
YDL_OPTIONS = {'format':"bestaudio"}
vc = ctx.voice_client
await ctx.send("Searching for the requested song")
with youtube_dl.YoutubeDL(YDL_OPTIONS) as ydl:
info = ydl.extract_info(url, download=False)
videotitle = ydl.prepare_filename(info)
url2 = info['formats'][0]['url']
source = await discord.FFmpegOpusAudio.from_probe(url2, **FFMPEG_OPTIONS)
vc.play(source)
await ctx.send(f"Playing {videotitle}")
This works but i want the output to be changed. Right now i get this:
MIX TECH HOUSE 2020 #8 (Camelphat, Torren Foot, Cardi B, Pax, Muus, Kevin McKay...)-RN7mbUBzUJw.m4a
I want the part "RN7mbUBzUJw.m4a" not to be there. does any one know what goes wrong in the ydl.prepare_filename?
The function YoutubeDL.prepare_filename generates a string that's a valid file name, mangling the video title if necessary and including the video ID and extension. It seems you want just the video title, so you can just do the same thing that function does manually.
The way they get the video title is:
video_title = info_dict.get('title', info_dict.get('id', 'video'))
where info_dict is the dictionary returned by YoutubeDL.extract_info. So in your case
videotitle = info.get('title', 'Video with ID: '+info.get('id', 'unknown'))
should work.
I want my bot to play multiple songs after each other. All I have is my bot playing one song and then stopping to do it. My Code so far:
#bot.command()
async def startq(ctx):
channel = bot.get_channel(705831663497904211)
vc = await channel.connect()
vc.play(discord.FFmpegPCMAudio("E:\Programmieren\Programmieren\Disc-Bot\music2.mp3"))
You should add the songs you want in the songs list, after that it should play all the songs.
You can also scan all the files in the folder and add them to the list using glob.
Note: I have not tried it, but it should work theoretically.
import glob
#bot.command()
async def startq(ctx):
channel = bot.get_channel(705831663497904211)
vc = await channel.connect()
songs = ["music2.mp3","music1.mp3"]
# or check all files in folder
songs = glob.glob("E:\Programmieren\Programmieren\Disc-Bot\*.mp3")
for song in songs:
vc.play(discord.FFmpegPCMAudio(f"E:\Programmieren\Programmieren\Disc-Bot\{song}"))
while vc.is_playing():
await asyncio.sleep(1)
Here is my code for my Discord bot's play function:
#bot.command()
async def play(ctx, url:str = None): # DOESN'T WORK, DOWNLOADS BUT DOESN'T PLAY
...
ytdl_options = {
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
}
ytdl = youtube_dl.YoutubeDL(ytdl_options)
loop = asyncio.get_event_loop()
data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url))
if 'entries' in data:
data = data['entries'][0] # taking top item from a YT playlist
source = ytdl.prepare_filename(data)
vc.play(discord.FFmpegPCMAudio(source)) #play audio
print(vc.is_playing())
await ctx.send("Now playing: " + data.get('title')) #now playing
asyncio.sleep(3)
print(vc.is_playing())
When I call the function, it runs fine and downloads the video from youtube-dl, then sends the message "Now playing: X", but doesn't play any audio.
I added the two print(vc.is_playing()) lines and what is returned in the shell is:
True
False
So clearly it tries to play the song but immediately fails.
There are no exceptions thrown, so I can't understand why FFmpeg doesn't want to play the audio.
I get the following error while I'm playing YouTube audio with my bot
[tls # 0000024ef8c4d480] Error in the pull function.
[matroska,webm # 0000024ef8c4a400] Read error
[tls # 0000024ef8c4d480] The specified session has been invalidated for some reason.
Last message repeated 1 times
It seems like YouTube links expire? I don't really know but I need to fix this issue. This is my code:
class YTDLSource(discord.PCMVolumeTransformer):
def __init__(self, source, *, data, requester):
super().__init__(source)
self.requester = requester
self.title = data['title']
self.description = data['description']
self.uploader = data['uploader']
self.duration = data['duration']
self.web_url = data['webpage_url']
self.thumbnail = data['thumbnail']
def __getitem__(self, item: str):
return self.__getattribute__(item)
#classmethod
async def create_source(cls, ctx, player, search: str, *, loop, download=True):
async with ctx.typing():
loop = loop or asyncio.get_event_loop()
to_run = partial(ytdl.extract_info, url=search, download=download)
raw_data = await loop.run_in_executor(None, to_run)
if 'entries' in raw_data:
# take first item from a playlist
if len(raw_data['entries']) == 1:
data = raw_data['entries'][0]
else:
data = raw_data['entries']
#loops entries to grab each video_url
total_duration = 0
try:
for i in data:
webpage = i['webpage_url']
title = i['title']
description = i['description']
uploader = i['uploader']
duration = i['duration']
thumbnail = i['thumbnail']
total_duration += duration
if download:
source = ytdl.prepare_filename(i)
source = cls(discord.FFmpegPCMAudio(source), data=i, requester=ctx.author)
else:
source = {'webpage_url': webpage, 'requester': ctx.author, 'title': title, 'uploader': uploader, 'description': description, 'duration': duration, 'thumbnail': thumbnail}
player.queue.append(source)
except Exception as e:
print(e)
return
embed=discord.Embed(title="Playlist", description="Queued", color=0x30a4fb, timestamp=datetime.now(timezone.utc))
embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url)
embed.set_thumbnail(url=data[0]['thumbnail'])
embed.add_field(name=raw_data['title'], value=f"{len(data)} videos queued.", inline=True)
embed.set_footer(text=raw_data["uploader"] + ' - ' + '{0[0]}m {0[1]}s'.format(divmod(total_duration, 60)))
await ctx.send(embed=embed)
return
embed=discord.Embed(title="Playlist", description="Queued", color=0x30a4fb, timestamp=datetime.now(timezone.utc))
embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url)
embed.set_thumbnail(url=data['thumbnail'])
embed.add_field(name=data['title'], value=(data["description"][:72] + (data["description"][72:] and '...')), inline=True)
embed.set_footer(text=data["uploader"] + ' - ' + '{0[0]}m {0[1]}s'.format(divmod(data["duration"], 60)))
await ctx.send(embed=embed)
if download:
source = ytdl.prepare_filename(data)
else:
source = {'webpage_url': data['webpage_url'], 'requester': ctx.author, 'title': data['title'], 'uploader': data['uploader'], 'description': data['description'], 'duration': data['duration'], 'thumbnail': data['thumbnail']}
player.queue.append(source)
return
source = cls(discord.FFmpegPCMAudio(source), data=data, requester=ctx.author)
player.queue.append(source)
#classmethod
async def regather_stream(cls, data, *, loop):
loop = loop or asyncio.get_event_loop()
requester = data['requester']
to_run = partial(ytdl.extract_info, url=data['webpage_url'], download=True)
data = await loop.run_in_executor(None, to_run)
return(cls(discord.FFmpegPCMAudio(data['url']), data=data, requester=requester))
I'm using the rewrite branch of discord.py for the bot.
I'm not sure if I need to provide more details? Please let me know, I really need to get this fixed...
In fact it isn't really a problem with your code (and many people complain of this error).
This is just a possible issue when streaming a video. If you absolutely want to stream it, you have to accept this as a potential issue. Note how (almost) every music bots set limitations for the video/music you want to listen to.
If you need to ensure you do not get this issue, you have to fully download the music. (Which will also make the bot loading longer before playing).
Would you be able to post all your code? i may have a solution for you if i was able to see the whole code.
the solution i would recomend is to download the soong and then delete it after.
You could set your download to true and then add this in your player_loop
try:
# We are no longer playing this song...so, lets delete it!
with YoutubeDL(ytdlopts) as ydl:
info = ydl.extract_info(source.web_url, download=False)
filename = ydl.prepare_filename(info)
try:
if os.path.exists(filename):
os.remove(filename)
else:
pass
except Exception as E:
print(E)
await self.np.delete()
except discord.HTTPException:
pass
bit botched but could be cleaned up, this was the best solution i have found for me.