Skip to content

Commit

Permalink
Merge pull request #11 from rahulgautam21/dev
Browse files Browse the repository at this point in the history
Merging dev to main
  • Loading branch information
rahulgautam21 authored Oct 8, 2022
2 parents 7a164e6 + e86824e commit 976ddda
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 9 deletions.
1 change: 1 addition & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ svishwa2@ncsu.edu
sbuddai@ncsu.edu
sngudipa@ncsu.edu
cchetan2@ncsu.edu
sdua2@ncsu.edu
rgautam3@ncsu.edu
```
Expand Down
8 changes: 6 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
pytest==7.1.2
pandas
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
46 changes: 46 additions & 0 deletions src/bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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
from songs_queue import Songs_Queue
from songs_cog import Songs


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)


@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})'
# )
voice_channel = client.get_channel(VOICE_CHANNEL_ID)
if client.user not in voice_channel.members:
await voice_channel.connect()
await client.load_extension("songs_cog")


@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)
await client.process_commands(message)

client.run(TOKEN)
44 changes: 37 additions & 7 deletions src/get_all.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
import pandas as pd
import random


#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

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
138 changes: 138 additions & 0 deletions src/songs_cog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
from multiprocessing.util import debug
import discord
from numpy import empty_like
from get_all import *
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.")

async def handle_empty_queue(self, ctx):
try:
songs_queue
except NameError:
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 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):
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):
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
global songs_queue
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))
30 changes: 30 additions & 0 deletions src/songs_queue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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
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]

def prev_song(self):
self.index -= 1
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)
20 changes: 20 additions & 0 deletions src/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from youtubesearchpython import VideosSearch

from get_all import filtered_songs


def searchSong(name_song):
print(name_song)
videosSearch = VideosSearch(name_song, limit=1)
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

0 comments on commit 976ddda

Please sign in to comment.