From 061ac6e2acc343ec0f709c34ef84c5b39e834819 Mon Sep 17 00:00:00 2001 From: Shubham Dua Date: Mon, 26 Sep 2022 16:29:29 -0400 Subject: [PATCH 1/9] Adding bot.py --- src/bot.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/get_all.py | 13 ++++++------ 2 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 src/bot.py diff --git a/src/bot.py b/src/bot.py new file mode 100644 index 0000000..78d8286 --- /dev/null +++ b/src/bot.py @@ -0,0 +1,54 @@ +import discord +from get_all import * +import re + + +# TOKEN = os.getenv('DISCORD_TOKEN') +TOKEN = "MTAyMzc2MDAzNzIwMzY4MTI5MA.GhRFer.qPgFUqt7o8677QozvQdGK8LcCO3U5IjQirBD2Y" +intents = discord.Intents.all() +intents.members = True +client = discord.Client(intents=intents) + +all_songs = filtered_songs()[["title", "artist", "top genre"]] + +def random_ten(): + ten_random_songs = (all_songs.sample(frac=1).groupby('top genre').head(1)).sample(10) + return ten_random_songs + +@client.event +async def on_ready(): + for guild in client.guilds: + print(guild.name) + + print( + f'{client.user} is connected to the following guild:\n' + f'{guild.name}(id: {guild.id})' + ) + + bot_message = "Select upto three songs by typing in the serial number: " + i = 0 + ten_random_songs = random_ten() + for ele in zip(ten_random_songs["title"], ten_random_songs["artist"]): + bot_message += "\n" + str(i) + " " + str(ele[0]) + " By "+ str(ele[1]) + i+=1 + + bot_message2 = "If you want to change the selection of songs, type in any alphabet" + + await client.get_channel(1017135653789646850).send(bot_message) + await client.get_channel(1017135653789646850).send(bot_message2) + +@client.event +async def on_message(message): + if message.author == client.user: + return + options = set() + + if message.channel.name == 'general': + user_message = str(message.content) + options = set(re.findall(r'\d', user_message)) + if options: + print(options) + else : + await on_ready() + +client.run(TOKEN) \ No newline at end of file diff --git a/src/get_all.py b/src/get_all.py index 4642a53..3d07f01 100644 --- a/src/get_all.py +++ b/src/get_all.py @@ -1,13 +1,12 @@ import pandas as pd - -#add pagination support -def get_all_songs(self): +def filtered_songs(): all_songs = pd.read_csv("../data/songs.csv") - print(' Data has (rows, columns):', all_songs.shape) - - all_songs = all_songs.filter(["title", "artist", "year"]) - print(all_songs.head(10)) + all_songs = all_songs.filter(["title", "artist", "year", "top genre"]) return all_songs +def get_all_songs(): + all_songs = pd.read_csv("../data/songs.csv") + return all_songs + From b1eb8a5d51600c5209743f188b251b15f020d208 Mon Sep 17 00:00:00 2001 From: Shubham Dua Date: Mon, 26 Sep 2022 16:48:38 -0400 Subject: [PATCH 2/9] Fixing bot.py --- src/bot.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bot.py b/src/bot.py index 78d8286..497b7e6 100644 --- a/src/bot.py +++ b/src/bot.py @@ -1,10 +1,13 @@ import discord +import os from get_all import * import re +from dotenv import load_dotenv -# TOKEN = os.getenv('DISCORD_TOKEN') -TOKEN = "MTAyMzc2MDAzNzIwMzY4MTI5MA.GhRFer.qPgFUqt7o8677QozvQdGK8LcCO3U5IjQirBD2Y" +load_dotenv('.env') +TOKEN = os.getenv('DISCORD_TOKEN') +# TOKEN = "MTAyMzc2MDAzNzIwMzY4MTI5MA.Gww-Na.W3xIuZd2Fcd_GwD7QEW0YWDFQqZyEkAL5vzeb4" intents = discord.Intents.all() intents.members = True client = discord.Client(intents=intents) From 71ae8bc7699df6d0d526815b686eb2298e2c61fe Mon Sep 17 00:00:00 2001 From: Shubham Dua Date: Mon, 26 Sep 2022 16:54:29 -0400 Subject: [PATCH 3/9] Fixing bot.py --- src/bot.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bot.py b/src/bot.py index 497b7e6..328ef67 100644 --- a/src/bot.py +++ b/src/bot.py @@ -7,7 +7,6 @@ load_dotenv('.env') TOKEN = os.getenv('DISCORD_TOKEN') -# TOKEN = "MTAyMzc2MDAzNzIwMzY4MTI5MA.Gww-Na.W3xIuZd2Fcd_GwD7QEW0YWDFQqZyEkAL5vzeb4" intents = discord.Intents.all() intents.members = True client = discord.Client(intents=intents) @@ -28,7 +27,7 @@ async def on_ready(): f'{guild.name}(id: {guild.id})' ) - bot_message = "Select upto three songs by typing in the serial number: " + bot_message = "Select songs by typing in the serial number: " + "\n" + "-------------------------------------------------" i = 0 ten_random_songs = random_ten() for ele in zip(ten_random_songs["title"], ten_random_songs["artist"]): From f25249c608ba584def8c1246b2b75742087e1475 Mon Sep 17 00:00:00 2001 From: rgautam3 Date: Mon, 3 Oct 2022 22:52:45 -0400 Subject: [PATCH 4/9] Fix in requirements --- requirements.txt | 6 ++++-- src/bot.py | 23 ++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/requirements.txt b/requirements.txt index a880097..52bc924 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ -pytest==7.1.2 -pandas +discord.py==2.0.1 +pandas==1.4.2 +python-dotenv==0.21.0 +setuptools==63.2.0 diff --git a/src/bot.py b/src/bot.py index 328ef67..6168199 100644 --- a/src/bot.py +++ b/src/bot.py @@ -5,7 +5,7 @@ from dotenv import load_dotenv -load_dotenv('.env') +load_dotenv('../.env') TOKEN = os.getenv('DISCORD_TOKEN') intents = discord.Intents.all() intents.members = True @@ -13,10 +13,13 @@ all_songs = filtered_songs()[["title", "artist", "top genre"]] + def random_ten(): - ten_random_songs = (all_songs.sample(frac=1).groupby('top genre').head(1)).sample(10) + ten_random_songs = (all_songs.sample( + frac=1).groupby('top genre').head(1)).sample(10) return ten_random_songs + @client.event async def on_ready(): for guild in client.guilds: @@ -27,18 +30,20 @@ async def on_ready(): f'{guild.name}(id: {guild.id})' ) - bot_message = "Select songs by typing in the serial number: " + "\n" + "-------------------------------------------------" + bot_message = "Select songs by typing in the serial number: " + \ + "\n" + "-------------------------------------------------" i = 0 ten_random_songs = random_ten() for ele in zip(ten_random_songs["title"], ten_random_songs["artist"]): - bot_message += "\n" + str(i) + " " + str(ele[0]) + " By "+ str(ele[1]) - i+=1 - + bot_message += "\n" + str(i) + " " + str(ele[0]) + " By " + str(ele[1]) + i += 1 + bot_message2 = "If you want to change the selection of songs, type in any alphabet" await client.get_channel(1017135653789646850).send(bot_message) await client.get_channel(1017135653789646850).send(bot_message2) + @client.event async def on_message(message): if message.author == client.user: @@ -50,7 +55,7 @@ async def on_message(message): options = set(re.findall(r'\d', user_message)) if options: print(options) - else : + else: await on_ready() - -client.run(TOKEN) \ No newline at end of file + +client.run(TOKEN) From db94b9132b891dc65e5068f00cf8af8e4f2dbbfc Mon Sep 17 00:00:00 2001 From: rgautam3 Date: Tue, 4 Oct 2022 15:53:02 -0400 Subject: [PATCH 5/9] Changes for playing songs --- INSTALL.md | 1 + requirements.txt | 2 ++ src/bot.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++- src/utils.py | 9 ++++++ src/ytdl.py | 47 ++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/utils.py create mode 100644 src/ytdl.py diff --git a/INSTALL.md b/INSTALL.md index 286a7fb..be4d1d9 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -6,6 +6,7 @@ Installation Guides: * [Git Installation Guide](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) * [IDE Installation Guide (Pycharm)](https://docs.docker.com/get-docker/](https://www.jetbrains.com/help/pycharm/installation-guide.html) + * Install FFMPEG from https://www.gyan.dev/ffmpeg/builds, extract it and add it to your path ## 2. Running Code diff --git a/requirements.txt b/requirements.txt index 52bc924..06a5295 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,5 @@ discord.py==2.0.1 pandas==1.4.2 python-dotenv==0.21.0 setuptools==63.2.0 +youtube_dl==2021.12.17 +youtube_search_python==1.6.6 diff --git a/src/bot.py b/src/bot.py index 6168199..2605627 100644 --- a/src/bot.py +++ b/src/bot.py @@ -3,13 +3,17 @@ from get_all import * import re from dotenv import load_dotenv +from discord.ext import commands, tasks +from utils import searchSong +from ytdl import YTDLSource load_dotenv('../.env') TOKEN = os.getenv('DISCORD_TOKEN') intents = discord.Intents.all() intents.members = True -client = discord.Client(intents=intents) +client = commands.Bot(command_prefix='/', intents=intents) + all_songs = filtered_songs()[["title", "artist", "top genre"]] @@ -20,6 +24,70 @@ def random_ten(): return ten_random_songs +@client.command(name='join', help='Tells the bot to join the voice channel') +async def join(ctx): + if not ctx.message.author.voice: + await ctx.send("{} is not connected to a voice channel".format(ctx.message.author.name)) + return + else: + channel = ctx.message.author.voice.channel + await channel.connect() + + +@client.command(name='leave', help='To make the bot leave the voice channel') +async def leave(ctx): + voice_client = ctx.message.guild.voice_client + if voice_client.is_connected(): + await voice_client.disconnect() + else: + await ctx.send("The bot is not connected to a voice channel.") + + +@client.command(name='play_song', help='To play song') +async def play(ctx): + user_message = str(ctx.message.content) + song_name = user_message.split(' ', 1)[1] + try: + server = ctx.message.guild + voice_channel = server.voice_client + url = searchSong(song_name) + print(url) + async with ctx.typing(): + filename = await YTDLSource.from_url(url, loop=client.loop) + voice_channel.play(discord.FFmpegPCMAudio(source=filename)) + await ctx.send('**Now playing:** {}'.format(filename)) + except Exception as e: + print(e) + await ctx.send("The bot is not connected to a voice channel.") + + +@client.command(name='pause', help='This command pauses the song') +async def pause(ctx): + voice_client = ctx.message.guild.voice_client + if voice_client.is_playing(): + await voice_client.pause() + else: + await ctx.send("The bot is not playing anything at the moment.") + + +@client.command(name='resume', help='Resumes the song') +async def resume(ctx): + voice_client = ctx.message.guild.voice_client + if voice_client.is_paused(): + await voice_client.resume() + else: + await ctx.send("The bot was not playing anything before this. Use play_song command") + + +@client.command(name='stop', help='Stops the song') +async def stop(ctx): + voice_client = ctx.message.guild.voice_client + if voice_client.is_playing(): + await voice_client.stop() + else: + await ctx.send("The bot is not playing anything at the moment.") + + @client.event async def on_ready(): for guild in client.guilds: @@ -57,5 +125,6 @@ async def on_message(message): print(options) else: await on_ready() + await client.process_commands(message) client.run(TOKEN) diff --git a/src/utils.py b/src/utils.py new file mode 100644 index 0000000..454d172 --- /dev/null +++ b/src/utils.py @@ -0,0 +1,9 @@ +from youtubesearchpython import VideosSearch + + +def searchSong(name_song): + print(name_song) + videosSearch = VideosSearch(name_song, limit=1) + result = videosSearch.result() + link = result['result'][0]['link'] + return link diff --git a/src/ytdl.py b/src/ytdl.py new file mode 100644 index 0000000..1b758aa --- /dev/null +++ b/src/ytdl.py @@ -0,0 +1,47 @@ +import discord +import os +from get_all import * +import re +from dotenv import load_dotenv +from discord.ext import commands, tasks +import youtube_dl + +youtube_dl.utils.bug_reports_message = lambda: '' + +ytdl_format_options = { + 'format': 'bestaudio/best', + 'restrictfilenames': True, + 'noplaylist': True, + 'nocheckcertificate': True, + 'ignoreerrors': False, + 'logtostderr': False, + 'quiet': True, + 'no_warnings': True, + 'default_search': 'auto', + # bind to ipv4 since ipv6 addresses cause issues sometimes + 'source_address': '0.0.0.0' +} + +ffmpeg_options = { + 'options': '-vn' +} + +ytdl = youtube_dl.YoutubeDL(ytdl_format_options) + + +class YTDLSource(discord.PCMVolumeTransformer): + def __init__(self, source, *, data, volume=0.5): + super().__init__(source, volume) + self.data = data + self.title = data.get('title') + self.url = "" + + @classmethod + async def from_url(cls, url, *, loop=None, stream=False): + loop = loop or asyncio.get_event_loop() + data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream)) + if 'entries' in data: + # take first item from a playlist + data = data['entries'][0] + filename = data['title'] if stream else ytdl.prepare_filename(data) + return filename From de6f97f560d6b1bcbcaf51ba442a99d2fbfcff48 Mon Sep 17 00:00:00 2001 From: rgautam3 Date: Thu, 6 Oct 2022 16:14:27 -0400 Subject: [PATCH 6/9] Changes for streaming songs and queuing songs --- src/bot.py | 132 +++++++++++++++++++++++++++++---------------- src/songs_queue.py | 24 +++++++++ src/ytdl.py | 47 ---------------- 3 files changed, 110 insertions(+), 93 deletions(-) create mode 100644 src/songs_queue.py delete mode 100644 src/ytdl.py diff --git a/src/bot.py b/src/bot.py index 2605627..627952e 100644 --- a/src/bot.py +++ b/src/bot.py @@ -1,19 +1,25 @@ +from multiprocessing.util import debug import discord import os from get_all import * import re from dotenv import load_dotenv -from discord.ext import commands, tasks +from discord.ext import commands from utils import searchSong -from ytdl import YTDLSource +from songs_queue import Songs_Queue +import youtube_dl load_dotenv('../.env') TOKEN = os.getenv('DISCORD_TOKEN') +# This can be obtained using ctx.message.author.voice.channel +VOICE_CHANNEL_ID = 1017135653789646851 intents = discord.Intents.all() intents.members = True client = commands.Bot(command_prefix='/', intents=intents) - +FFMPEG_OPTIONS = { + 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} +YDL_OPTIONS = {'format': 'bestaudio/best', 'noplaylist': 'True'} all_songs = filtered_songs()[["title", "artist", "top genre"]] @@ -24,43 +30,58 @@ def random_ten(): return ten_random_songs -@client.command(name='join', help='Tells the bot to join the voice channel') -async def join(ctx): - if not ctx.message.author.voice: - await ctx.send("{} is not connected to a voice channel".format(ctx.message.author.name)) - return - else: - channel = ctx.message.author.voice.channel - await channel.connect() - - -@client.command(name='leave', help='To make the bot leave the voice channel') -async def leave(ctx): - voice_client = ctx.message.guild.voice_client - if voice_client.is_connected(): - await voice_client.disconnect() - else: - await ctx.send("The bot is not connected to a voice channel.") - - @client.command(name='play_song', help='To play song') async def play(ctx): user_message = str(ctx.message.content) song_name = user_message.split(' ', 1)[1] + await play_song(song_name, ctx) + + +async def play_song(song_name, ctx): + # First stop whatever the bot is playing + await stop(ctx) try: server = ctx.message.guild voice_channel = server.voice_client url = searchSong(song_name) - print(url) async with ctx.typing(): - filename = await YTDLSource.from_url(url, loop=client.loop) - voice_channel.play(discord.FFmpegPCMAudio(source=filename)) - await ctx.send('**Now playing:** {}'.format(filename)) + with youtube_dl.YoutubeDL(YDL_OPTIONS) as ydl: + info = ydl.extract_info(url, download=False) + I_URL = info['formats'][0]['url'] + source = await discord.FFmpegOpusAudio.from_probe(I_URL, **FFMPEG_OPTIONS) + voice_channel.play(source) + voice_channel.is_playing() + await ctx.send('**Now playing:** {}'.format(song_name)) except Exception as e: - print(e) await ctx.send("The bot is not connected to a voice channel.") +@client.command(name='next_song', help='To play next song in queue') +async def play(ctx): + try: + songs_queue + except NameError: + await ctx.send("No recommendations present. First generate recommendations") + return + if songs_queue.get_len() == 0: + await ctx.send("No recommendations present. First generate recommendations") + return + await play_song(songs_queue.next_song(), ctx) + + +@client.command(name='prev_song', help='To play prev song in queue') +async def play(ctx): + try: + songs_queue + except NameError: + await ctx.send("No recommendations present. First generate recommendations") + return + if songs_queue.get_len() == 0: + await ctx.send("No recommendations present. First generate recommendations") + return + await play_song(songs_queue.prev_song(), ctx) + + @client.command(name='pause', help='This command pauses the song') async def pause(ctx): voice_client = ctx.message.guild.voice_client @@ -83,33 +104,52 @@ async def resume(ctx): async def stop(ctx): voice_client = ctx.message.guild.voice_client if voice_client.is_playing(): - await voice_client.stop() + voice_client.stop() else: await ctx.send("The bot is not playing anything at the moment.") @client.event async def on_ready(): - for guild in client.guilds: - print(guild.name) - - print( - f'{client.user} is connected to the following guild:\n' - f'{guild.name}(id: {guild.id})' - ) - - bot_message = "Select songs by typing in the serial number: " + \ - "\n" + "-------------------------------------------------" - i = 0 + # for guild in client.guilds: + # print(guild.name) + # print( + # f'{client.user} is connected to the following guild:\n' + # f'{guild.name}(id: {guild.id})' + # ) + voice_channel = client.get_channel(VOICE_CHANNEL_ID) + if client.user not in voice_channel.members: + await voice_channel.connect() + + +@client.command(name='poll', help='Poll for recommendation') +async def poll(ctx): + reactions = ['👍', '👎'] + selected_songs = [] + count = 0 + bot_message = "Select song preferences by reaction '👍' or '👎' to the choices. \nSelect 3 songs" + await ctx.send(bot_message) ten_random_songs = random_ten() for ele in zip(ten_random_songs["title"], ten_random_songs["artist"]): - bot_message += "\n" + str(i) + " " + str(ele[0]) + " By " + str(ele[1]) - i += 1 - - bot_message2 = "If you want to change the selection of songs, type in any alphabet" - - await client.get_channel(1017135653789646850).send(bot_message) - await client.get_channel(1017135653789646850).send(bot_message2) + bot_message = str(ele[0]) + " By " + str(ele[1]) + description = [] + poll_embed = discord.Embed( + title=bot_message, color=0x31FF00, description=''.join(description)) + react_message = await ctx.send(embed=poll_embed) + for reaction in reactions[:len(reactions)]: + await react_message.add_reaction(reaction) + res, user = await client.wait_for('reaction_add') + if (res.emoji == u'👍'): + selected_songs.append(str(ele[0])) + count += 1 + if (count == 3): + bot_message = "Selected songs are : " + ' , '.join(selected_songs) + await ctx.send(bot_message) + break + # TODO: Send the selected songs and get preferences + # For now setting it manually + global songs_queue + songs_queue = Songs_Queue(ten_random_songs['title'].tolist()) @client.event diff --git a/src/songs_queue.py b/src/songs_queue.py new file mode 100644 index 0000000..bb4bf56 --- /dev/null +++ b/src/songs_queue.py @@ -0,0 +1,24 @@ +# This queue will store all the recommendation of the songs + +class Songs_Queue(): + def __init__(self, song_names): + self.queue = song_names + self.index = 0 + + def next_song(self): + print(self.queue) + if (self.index == len(self.queue)): + self.index = 0 + val = self.index + self.index += 1 + return self.queue[val] + + def prev_song(self): + self.index -= 1 + if (self.index <= 0): + self.index = len(self.queue) - 1 + val = self.index + return self.queue[val] + + def get_len(self): + return len(self.queue) diff --git a/src/ytdl.py b/src/ytdl.py deleted file mode 100644 index 1b758aa..0000000 --- a/src/ytdl.py +++ /dev/null @@ -1,47 +0,0 @@ -import discord -import os -from get_all import * -import re -from dotenv import load_dotenv -from discord.ext import commands, tasks -import youtube_dl - -youtube_dl.utils.bug_reports_message = lambda: '' - -ytdl_format_options = { - 'format': 'bestaudio/best', - 'restrictfilenames': True, - 'noplaylist': True, - 'nocheckcertificate': True, - 'ignoreerrors': False, - 'logtostderr': False, - 'quiet': True, - 'no_warnings': True, - 'default_search': 'auto', - # bind to ipv4 since ipv6 addresses cause issues sometimes - 'source_address': '0.0.0.0' -} - -ffmpeg_options = { - 'options': '-vn' -} - -ytdl = youtube_dl.YoutubeDL(ytdl_format_options) - - -class YTDLSource(discord.PCMVolumeTransformer): - def __init__(self, source, *, data, volume=0.5): - super().__init__(source, volume) - self.data = data - self.title = data.get('title') - self.url = "" - - @classmethod - async def from_url(cls, url, *, loop=None, stream=False): - loop = loop or asyncio.get_event_loop() - data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream)) - if 'entries' in data: - # take first item from a playlist - data = data['entries'][0] - filename = data['title'] if stream else ytdl.prepare_filename(data) - return filename From c3343f2ea1118761962c18b51daec1dfe23d2ddc Mon Sep 17 00:00:00 2001 From: rgautam3 Date: Thu, 6 Oct 2022 18:32:45 -0400 Subject: [PATCH 7/9] Reorganization into cogs --- src/bot.py | 128 +---------------------------------------------- src/songs_cog.py | 127 ++++++++++++++++++++++++++++++++++++++++++++++ src/utils.py | 11 ++++ 3 files changed, 140 insertions(+), 126 deletions(-) create mode 100644 src/songs_cog.py diff --git a/src/bot.py b/src/bot.py index 627952e..bc81a4d 100644 --- a/src/bot.py +++ b/src/bot.py @@ -7,7 +7,7 @@ from discord.ext import commands from utils import searchSong from songs_queue import Songs_Queue -import youtube_dl +from songs_cog import Songs load_dotenv('../.env') @@ -17,96 +17,6 @@ intents = discord.Intents.all() intents.members = True client = commands.Bot(command_prefix='/', intents=intents) -FFMPEG_OPTIONS = { - 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} -YDL_OPTIONS = {'format': 'bestaudio/best', 'noplaylist': 'True'} - -all_songs = filtered_songs()[["title", "artist", "top genre"]] - - -def random_ten(): - ten_random_songs = (all_songs.sample( - frac=1).groupby('top genre').head(1)).sample(10) - return ten_random_songs - - -@client.command(name='play_song', help='To play song') -async def play(ctx): - user_message = str(ctx.message.content) - song_name = user_message.split(' ', 1)[1] - await play_song(song_name, ctx) - - -async def play_song(song_name, ctx): - # First stop whatever the bot is playing - await stop(ctx) - try: - server = ctx.message.guild - voice_channel = server.voice_client - url = searchSong(song_name) - async with ctx.typing(): - with youtube_dl.YoutubeDL(YDL_OPTIONS) as ydl: - info = ydl.extract_info(url, download=False) - I_URL = info['formats'][0]['url'] - source = await discord.FFmpegOpusAudio.from_probe(I_URL, **FFMPEG_OPTIONS) - voice_channel.play(source) - voice_channel.is_playing() - await ctx.send('**Now playing:** {}'.format(song_name)) - except Exception as e: - await ctx.send("The bot is not connected to a voice channel.") - - -@client.command(name='next_song', help='To play next song in queue') -async def play(ctx): - try: - songs_queue - except NameError: - await ctx.send("No recommendations present. First generate recommendations") - return - if songs_queue.get_len() == 0: - await ctx.send("No recommendations present. First generate recommendations") - return - await play_song(songs_queue.next_song(), ctx) - - -@client.command(name='prev_song', help='To play prev song in queue') -async def play(ctx): - try: - songs_queue - except NameError: - await ctx.send("No recommendations present. First generate recommendations") - return - if songs_queue.get_len() == 0: - await ctx.send("No recommendations present. First generate recommendations") - return - await play_song(songs_queue.prev_song(), ctx) - - -@client.command(name='pause', help='This command pauses the song') -async def pause(ctx): - voice_client = ctx.message.guild.voice_client - if voice_client.is_playing(): - await voice_client.pause() - else: - await ctx.send("The bot is not playing anything at the moment.") - - -@client.command(name='resume', help='Resumes the song') -async def resume(ctx): - voice_client = ctx.message.guild.voice_client - if voice_client.is_paused(): - await voice_client.resume() - else: - await ctx.send("The bot was not playing anything before this. Use play_song command") - - -@client.command(name='stop', help='Stops the song') -async def stop(ctx): - voice_client = ctx.message.guild.voice_client - if voice_client.is_playing(): - voice_client.stop() - else: - await ctx.send("The bot is not playing anything at the moment.") @client.event @@ -120,36 +30,7 @@ async def on_ready(): voice_channel = client.get_channel(VOICE_CHANNEL_ID) if client.user not in voice_channel.members: await voice_channel.connect() - - -@client.command(name='poll', help='Poll for recommendation') -async def poll(ctx): - reactions = ['👍', '👎'] - selected_songs = [] - count = 0 - bot_message = "Select song preferences by reaction '👍' or '👎' to the choices. \nSelect 3 songs" - await ctx.send(bot_message) - ten_random_songs = random_ten() - for ele in zip(ten_random_songs["title"], ten_random_songs["artist"]): - bot_message = str(ele[0]) + " By " + str(ele[1]) - description = [] - poll_embed = discord.Embed( - title=bot_message, color=0x31FF00, description=''.join(description)) - react_message = await ctx.send(embed=poll_embed) - for reaction in reactions[:len(reactions)]: - await react_message.add_reaction(reaction) - res, user = await client.wait_for('reaction_add') - if (res.emoji == u'👍'): - selected_songs.append(str(ele[0])) - count += 1 - if (count == 3): - bot_message = "Selected songs are : " + ' , '.join(selected_songs) - await ctx.send(bot_message) - break - # TODO: Send the selected songs and get preferences - # For now setting it manually - global songs_queue - songs_queue = Songs_Queue(ten_random_songs['title'].tolist()) + await client.load_extension("songs_cog") @client.event @@ -160,11 +41,6 @@ async def on_message(message): if message.channel.name == 'general': user_message = str(message.content) - options = set(re.findall(r'\d', user_message)) - if options: - print(options) - else: - await on_ready() await client.process_commands(message) client.run(TOKEN) diff --git a/src/songs_cog.py b/src/songs_cog.py new file mode 100644 index 0000000..857512d --- /dev/null +++ b/src/songs_cog.py @@ -0,0 +1,127 @@ +from multiprocessing.util import debug +import discord +import os +from get_all import * +import re +from dotenv import load_dotenv +from discord.ext import commands +from utils import searchSong, random_ten +from songs_queue import Songs_Queue +import youtube_dl + +FFMPEG_OPTIONS = { + 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} +YDL_OPTIONS = {'format': 'bestaudio/best', 'noplaylist': 'True'} + + +class Songs(commands.Cog): + + def __init__(self, bot): + self.bot = bot + + @commands.command(name='resume', help='Resumes the song') + async def resume(self, ctx): + voice_client = ctx.message.guild.voice_client + if voice_client.is_paused(): + await voice_client.resume() + else: + await ctx.send("The bot was not playing anything before this. Use play command") + + @commands.command(name='play_custom', help='To play custom song') + async def play_custom(self, ctx): + user_message = str(ctx.message.content) + song_name = user_message.split(' ', 1)[1] + await self.play_song(song_name, ctx) + + @commands.command(name='stop', help='Stops the song') + async def stop(self, ctx): + voice_client = ctx.message.guild.voice_client + if voice_client.is_playing(): + voice_client.stop() + else: + await ctx.send("The bot is not playing anything at the moment.") + + async def play_song(self, song_name, ctx): + # First stop whatever the bot is playing + await self.stop(ctx) + try: + server = ctx.message.guild + voice_channel = server.voice_client + url = searchSong(song_name) + async with ctx.typing(): + with youtube_dl.YoutubeDL(YDL_OPTIONS) as ydl: + info = ydl.extract_info(url, download=False) + I_URL = info['formats'][0]['url'] + source = await discord.FFmpegOpusAudio.from_probe(I_URL, **FFMPEG_OPTIONS) + voice_channel.play(source) + voice_channel.is_playing() + await ctx.send('**Now playing:** {}'.format(song_name)) + except Exception as e: + await ctx.send("The bot is not connected to a voice channel.") + + @commands.command(name='next_song', help='To play next song in queue') + async def next_song(self, ctx): + try: + songs_queue + except NameError: + await ctx.send("No recommendations present. First generate recommendations") + return + if songs_queue.get_len() == 0: + await ctx.send("No recommendations present. First generate recommendations") + return + await self.play_song(songs_queue.next_song(), ctx) + + @commands.command(name='prev_song', help='To play prev song in queue') + async def play(self, ctx): + try: + songs_queue + except NameError: + await ctx.send("No recommendations present. First generate recommendations") + return + if songs_queue.get_len() == 0: + await ctx.send("No recommendations present. First generate recommendations") + return + await self.play_song(songs_queue.prev_song(), ctx) + + @commands.command(name='pause', help='This command pauses the song') + async def pause(self, ctx): + voice_client = ctx.message.guild.voice_client + if voice_client.is_playing(): + await voice_client.pause() + else: + await ctx.send("The bot is not playing anything at the moment.") + + @commands.command(name='poll', help='Poll for recommendation') + async def poll(self, ctx): + reactions = ['👍', '👎'] + selected_songs = [] + count = 0 + bot_message = "Select song preferences by reaction '👍' or '👎' to the choices. \nSelect 3 songs" + await ctx.send(bot_message) + ten_random_songs = random_ten() + for ele in zip(ten_random_songs["title"], ten_random_songs["artist"]): + bot_message = str(ele[0]) + " By " + str(ele[1]) + description = [] + poll_embed = discord.Embed( + title=bot_message, color=0x31FF00, description=''.join(description)) + react_message = await ctx.send(embed=poll_embed) + for reaction in reactions[:len(reactions)]: + await react_message.add_reaction(reaction) + res, user = await self.bot.wait_for('reaction_add') + if (res.emoji == u'👍'): + selected_songs.append(str(ele[0])) + count += 1 + if (count == 3): + bot_message = "Selected songs are : " + \ + ' , '.join(selected_songs) + await ctx.send(bot_message) + break + # TODO: Send the selected songs and get preferences + # For now setting it manually + global songs_queue + songs_queue = Songs_Queue(ten_random_songs['title'].tolist()) + await self.play_song(songs_queue.next_song(), ctx) + + +async def setup(client): + await client.add_cog(Songs(client)) diff --git a/src/utils.py b/src/utils.py index 454d172..2f88cba 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,5 +1,7 @@ from youtubesearchpython import VideosSearch +from get_all import filtered_songs + def searchSong(name_song): print(name_song) @@ -7,3 +9,12 @@ def searchSong(name_song): result = videosSearch.result() link = result['result'][0]['link'] return link + + +all_songs = filtered_songs()[["title", "artist", "top genre"]] + + +def random_ten(): + ten_random_songs = (all_songs.sample( + frac=1).groupby('top genre').head(1)).sample(10) + return ten_random_songs From b9ade39ae50023163f96e5e957282a8ddf1077c3 Mon Sep 17 00:00:00 2001 From: Shubham Dua Date: Fri, 7 Oct 2022 04:18:05 -0400 Subject: [PATCH 8/9] Adding recommendation queue --- src/get_all.py | 33 +++++++++++++++++++++++++++- src/songs_cog.py | 55 +++++++++++++++++++++++++++------------------- src/songs_queue.py | 6 +++++ 3 files changed, 71 insertions(+), 23 deletions(-) diff --git a/src/get_all.py b/src/get_all.py index 3d07f01..6baa0ec 100644 --- a/src/get_all.py +++ b/src/get_all.py @@ -1,4 +1,5 @@ import pandas as pd +import random def filtered_songs(): all_songs = pd.read_csv("../data/songs.csv") @@ -9,4 +10,34 @@ def get_all_songs(): all_songs = pd.read_csv("../data/songs.csv") return all_songs - +def recommend(input_songs): + # removing all songs with count = 1 + songs = get_all_songs() + songs = songs.groupby('top genre').filter(lambda x : len(x)>0) + # creating dictionary of song titles and genre + playlist = dict(zip(songs['title'], songs['top genre'])) + # creating dictionary to count the frequency of each genre + freq = {} + for item in songs['top genre']: + if (item in freq): + freq[item] += 1 + else: + freq[item] = 1 + # create list of all songs from the input genre + selected_list = [] + output = [] + for input in input_songs: + if input in playlist.keys(): + for key, value in playlist.items(): + if playlist[input] == value: + selected_list.append(key) + selected_list.remove(input) + if (len(selected_list) >= 10): + output = random.sample(selected_list, 10) + else: + extra_songs = 10 - len(selected_list) + song_names = songs['title'].to_list() + song_names_filtered = [x for x in song_names if x not in selected_list] + selected_list.extend(random.sample(song_names_filtered, extra_songs)) + output = selected_list.copy() + return output diff --git a/src/songs_cog.py b/src/songs_cog.py index 857512d..105ca87 100644 --- a/src/songs_cog.py +++ b/src/songs_cog.py @@ -1,8 +1,7 @@ from multiprocessing.util import debug import discord -import os +from numpy import empty_like from get_all import * -import re from dotenv import load_dotenv from discord.ext import commands from utils import searchSong, random_ten @@ -59,29 +58,30 @@ async def play_song(self, song_name, ctx): except Exception as e: await ctx.send("The bot is not connected to a voice channel.") - @commands.command(name='next_song', help='To play next song in queue') - async def next_song(self, ctx): + async def handle_empty_queue(self, ctx): try: songs_queue except NameError: - await ctx.send("No recommendations present. First generate recommendations") - return + await ctx.send("No recommendations present. First generate recommendations using /poll") + return True if songs_queue.get_len() == 0: - await ctx.send("No recommendations present. First generate recommendations") - return - await self.play_song(songs_queue.next_song(), ctx) + await ctx.send("No recommendations present. First generate recommendations using /poll") + return True + return False + + + @commands.command(name='next_song', help='To play next song in queue') + async def next_song(self, ctx): + empty_queue = await self.handle_empty_queue(ctx) + if not empty_queue: + await self.play_song(songs_queue.next_song(), ctx) @commands.command(name='prev_song', help='To play prev song in queue') async def play(self, ctx): - try: - songs_queue - except NameError: - await ctx.send("No recommendations present. First generate recommendations") - return - if songs_queue.get_len() == 0: - await ctx.send("No recommendations present. First generate recommendations") - return - await self.play_song(songs_queue.prev_song(), ctx) + empty_queue = await self.handle_empty_queue(ctx) + if not empty_queue: + await self.play_song(songs_queue.prev_song(), ctx) + @commands.command(name='pause', help='This command pauses the song') async def pause(self, ctx): @@ -116,12 +116,23 @@ async def poll(self, ctx): ' , '.join(selected_songs) await ctx.send(bot_message) break - # TODO: Send the selected songs and get preferences - # For now setting it manually global songs_queue - songs_queue = Songs_Queue(ten_random_songs['title'].tolist()) + recommended_songs = recommend(selected_songs) + songs_queue = Songs_Queue(recommended_songs) await self.play_song(songs_queue.next_song(), ctx) + @commands.command(name='queue', help='Show active queue of recommendations') + async def queue(self, ctx): + empty_queue = await self.handle_empty_queue(ctx) + if not empty_queue: + queue,index = songs_queue.return_queue() + await ctx.send("Queue of recommendations: ") + for i in range(len(queue)): + if i == index: + await ctx.send("*" + queue[i]) + else: + await ctx.send(queue[i]) + async def setup(client): - await client.add_cog(Songs(client)) + await client.add_cog(Songs(client)) \ No newline at end of file diff --git a/src/songs_queue.py b/src/songs_queue.py index bb4bf56..588df4c 100644 --- a/src/songs_queue.py +++ b/src/songs_queue.py @@ -4,12 +4,14 @@ class Songs_Queue(): def __init__(self, song_names): self.queue = song_names self.index = 0 + self.current_index = 0 def next_song(self): print(self.queue) if (self.index == len(self.queue)): self.index = 0 val = self.index + self.current_index = val self.index += 1 return self.queue[val] @@ -18,7 +20,11 @@ def prev_song(self): if (self.index <= 0): self.index = len(self.queue) - 1 val = self.index + self.current_index = val return self.queue[val] def get_len(self): return len(self.queue) + + def return_queue(self): + return (self.queue, self.current_index) From e86824e52b08869a9be755dfc77ecd592c6a7fb7 Mon Sep 17 00:00:00 2001 From: Shubham Dua <67901145+Shubhamdua02@users.noreply.github.com> Date: Fri, 7 Oct 2022 05:28:32 -0400 Subject: [PATCH 9/9] Update SECURITY.md --- SECURITY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SECURITY.md b/SECURITY.md index b41e257..7674b97 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -19,6 +19,8 @@ svishwa2@ncsu.edu sbuddai@ncsu.edu sngudipa@ncsu.edu cchetan2@ncsu.edu +sdua2@ncsu.edu +rgautam3@ncsu.edu ```