🛠 fixed queue bugs, added spotify song support 🔊
This commit is contained in:
@@ -5,12 +5,49 @@ import cogs.music.util as util
|
|||||||
import cogs.music.queue as queue
|
import cogs.music.queue as queue
|
||||||
import cogs.music.translate as translate
|
import cogs.music.translate as translate
|
||||||
|
|
||||||
import datetime
|
|
||||||
import pytz
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
from cogs.music.help import music_help
|
from cogs.music.help import music_help
|
||||||
|
import discord
|
||||||
|
|
||||||
|
import spotipy
|
||||||
|
from spotipy.oauth2 import SpotifyClientCredentials
|
||||||
|
|
||||||
|
|
||||||
|
# Fix this pls
|
||||||
|
|
||||||
|
import json
|
||||||
|
#from .. import config
|
||||||
|
# Read data from JSON file in ./data/config.json
|
||||||
|
def read_data():
|
||||||
|
with open("./data/config.json", "r") as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
raise Exception("Could not load config data")
|
||||||
|
|
||||||
|
|
||||||
|
def get_spotify_creds():
|
||||||
|
data = read_data()
|
||||||
|
data = data.get("spotify")
|
||||||
|
|
||||||
|
SCID = data.get("SCID")
|
||||||
|
secret = data.get("SECRET")
|
||||||
|
|
||||||
|
return SCID, secret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# call play on ffmpeg exit
|
||||||
|
class AstroPlayer(discord.FFmpegPCMAudio):
|
||||||
|
def __init__(self, ctx, source, options) -> None:
|
||||||
|
#self.ctx = ctx
|
||||||
|
super().__init__(source, **options)
|
||||||
|
|
||||||
|
def _kill_process(self):
|
||||||
|
super()._kill_process()
|
||||||
|
#asyncio.create_task(play(self.ctx))
|
||||||
|
|
||||||
class music(commands.Cog):
|
class music(commands.Cog):
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
@@ -23,21 +60,16 @@ class music(commands.Cog):
|
|||||||
help_command.cog = self
|
help_command.cog = self
|
||||||
self.help_command = help_command
|
self.help_command = help_command
|
||||||
|
|
||||||
|
SCID, secret = get_spotify_creds()
|
||||||
|
# Authentication - without user
|
||||||
|
client_credentials_manager = SpotifyClientCredentials(client_id=SCID,
|
||||||
|
client_secret=secret)
|
||||||
|
|
||||||
|
self.sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
|
||||||
|
|
||||||
queue.initialize_tables()
|
queue.initialize_tables()
|
||||||
|
|
||||||
|
|
||||||
@commands.command(
|
|
||||||
help="Displays latency from the bot",
|
|
||||||
aliases=['delay'])
|
|
||||||
async def ping(self, ctx: Context):
|
|
||||||
start_time = datetime.datetime.now(pytz.utc)
|
|
||||||
end_time = ctx.message.created_at
|
|
||||||
|
|
||||||
delay = int((end_time - start_time).total_seconds() * 1000)
|
|
||||||
|
|
||||||
await ctx.send(f"Pong! `{delay}MS`")
|
|
||||||
|
|
||||||
|
|
||||||
@commands.command(
|
@commands.command(
|
||||||
help="Connects to your current voice channel",
|
help="Connects to your current voice channel",
|
||||||
aliases=['connect'])
|
aliases=['connect'])
|
||||||
@@ -53,7 +85,6 @@ class music(commands.Cog):
|
|||||||
await util.leave_vc(ctx)
|
await util.leave_vc(ctx)
|
||||||
await ctx.message.add_reaction('👍')
|
await ctx.message.add_reaction('👍')
|
||||||
|
|
||||||
|
|
||||||
@commands.command(
|
@commands.command(
|
||||||
help="Queues a song into the bot",
|
help="Queues a song into the bot",
|
||||||
aliases=['p'])
|
aliases=['p'])
|
||||||
@@ -65,14 +96,14 @@ class music(commands.Cog):
|
|||||||
|
|
||||||
server = ctx.guild.id
|
server = ctx.guild.id
|
||||||
|
|
||||||
await ctx.message.add_reaction('👍')
|
|
||||||
await util.join_vc(ctx)
|
await util.join_vc(ctx)
|
||||||
|
await ctx.message.add_reaction('👍')
|
||||||
|
|
||||||
msg = await ctx.send("Fetching song(s)...")
|
msg = await ctx.send("Fetching song(s)...")
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
#TODO potentially save requests before getting stream link
|
#TODO potentially save requests before getting stream link
|
||||||
# Grab video details such as title thumbnail duration
|
# Grab video details such as title thumbnail duration
|
||||||
audio = translate.main(url)
|
audio = translate.main(url, self.sp)
|
||||||
|
|
||||||
await msg.delete()
|
await msg.delete()
|
||||||
|
|
||||||
@@ -99,7 +130,6 @@ class music(commands.Cog):
|
|||||||
await queue.play(ctx)
|
await queue.play(ctx)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@commands.command(
|
@commands.command(
|
||||||
help="Display the current music queue",
|
help="Display the current music queue",
|
||||||
aliases=['q', 'songs'])
|
aliases=['q', 'songs'])
|
||||||
@@ -116,7 +146,7 @@ class music(commands.Cog):
|
|||||||
n, songs = await queue.grab_songs(server.id)
|
n, songs = await queue.grab_songs(server.id)
|
||||||
|
|
||||||
# Check once more
|
# Check once more
|
||||||
if len(songs) == 0:
|
if len(songs) == 0 and await queue.is_server_playing(ctx.guild.id) == False:
|
||||||
await ctx.send("🚫 This server has no queue currently. Start the party by queuing up a song!")
|
await ctx.send("🚫 This server has no queue currently. Start the party by queuing up a song!")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from http import server
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
@@ -22,7 +23,8 @@ def initialize_tables():
|
|||||||
# Create servers table if it doesn't exist
|
# Create servers table if it doesn't exist
|
||||||
cursor.execute('''CREATE TABLE IF NOT EXISTS servers (
|
cursor.execute('''CREATE TABLE IF NOT EXISTS servers (
|
||||||
server_id TEXT PRIMARY KEY,
|
server_id TEXT PRIMARY KEY,
|
||||||
is_playing INTEGER DEFAULT 0
|
is_playing INTEGER DEFAULT 0,
|
||||||
|
song_name TEXT
|
||||||
);''')
|
);''')
|
||||||
|
|
||||||
# Set all to not playing
|
# Set all to not playing
|
||||||
@@ -83,6 +85,34 @@ async def add_song(server_id, details, queued_by):
|
|||||||
return max_order_num
|
return max_order_num
|
||||||
|
|
||||||
|
|
||||||
|
# Pop song from server
|
||||||
|
async def pop(server_id):
|
||||||
|
# Connect to db
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# JUST INCASE!
|
||||||
|
await add_server(server_id, cursor, conn)
|
||||||
|
|
||||||
|
cursor.execute('''SELECT *
|
||||||
|
FROM songs
|
||||||
|
WHERE server_id = ?
|
||||||
|
ORDER BY position
|
||||||
|
LIMIT 1;''', (server_id,))
|
||||||
|
result = cursor.fetchone()
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if result == None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
await set_current_song(server_id, result[4])
|
||||||
|
await mark_song_as_finished(server_id, result[3])
|
||||||
|
|
||||||
|
return result[1]
|
||||||
|
|
||||||
|
|
||||||
# Add server to db if first time queuing
|
# Add server to db if first time queuing
|
||||||
async def add_server(server_id, cursor, conn):
|
async def add_server(server_id, cursor, conn):
|
||||||
# Check if the server exists
|
# Check if the server exists
|
||||||
@@ -116,6 +146,42 @@ async def mark_song_as_finished(server_id, order_num):
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
# set the current playing song of the server
|
||||||
|
async def set_current_song(server_id, title):
|
||||||
|
# Connect to the database
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
cursor.execute(''' UPDATE servers
|
||||||
|
SET song_name = ?
|
||||||
|
WHERE server_id = ?''',
|
||||||
|
(title, server_id))
|
||||||
|
|
||||||
|
# Close connection
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_current_song(server_id):
|
||||||
|
# Connect to the database
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
cursor.execute(''' SELECT song_name
|
||||||
|
FROM servers
|
||||||
|
WHERE server_id = ?
|
||||||
|
LIMIT 1;''',
|
||||||
|
(server_id,))
|
||||||
|
|
||||||
|
result = cursor.fetchone()
|
||||||
|
|
||||||
|
# Close connection
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return result[0]
|
||||||
|
|
||||||
|
|
||||||
# Grab max order from server
|
# Grab max order from server
|
||||||
async def get_max(server_id, cursor):
|
async def get_max(server_id, cursor):
|
||||||
cursor.execute(f"""
|
cursor.execute(f"""
|
||||||
@@ -131,35 +197,6 @@ async def get_max(server_id, cursor):
|
|||||||
return max_order_num
|
return max_order_num
|
||||||
|
|
||||||
|
|
||||||
# Pop song from server
|
|
||||||
async def pop(server_id):
|
|
||||||
# Connect to db
|
|
||||||
conn = sqlite3.connect(db_path)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# JUST INCASE!
|
|
||||||
await add_server(server_id, cursor, conn)
|
|
||||||
|
|
||||||
max_order = await get_max(server_id, cursor)
|
|
||||||
if max_order == -1:
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
return None
|
|
||||||
|
|
||||||
cursor.execute('''SELECT song_link
|
|
||||||
FROM songs
|
|
||||||
WHERE server_id = ? AND position = ?
|
|
||||||
''', (server_id, max_order))
|
|
||||||
result = cursor.fetchone()
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
await mark_song_as_finished(server_id, max_order)
|
|
||||||
|
|
||||||
return result[0]
|
|
||||||
|
|
||||||
|
|
||||||
# Sets the playing variable in a server to true or false
|
# Sets the playing variable in a server to true or false
|
||||||
async def update_server(server_id, playing: bool):
|
async def update_server(server_id, playing: bool):
|
||||||
# Connect to database
|
# Connect to database
|
||||||
@@ -259,7 +296,7 @@ async def play(ctx):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# else play next song and call play again
|
# else play next song and call play again
|
||||||
ctx.voice_client.play(
|
await ctx.voice_client.play(
|
||||||
AstroPlayer(ctx, url, FFMPEG_OPTS))
|
AstroPlayer(ctx, url, FFMPEG_OPTS))
|
||||||
|
|
||||||
# call play on ffmpeg exit
|
# call play on ffmpeg exit
|
||||||
@@ -270,4 +307,4 @@ class AstroPlayer(discord.FFmpegPCMAudio):
|
|||||||
|
|
||||||
def _kill_process(self):
|
def _kill_process(self):
|
||||||
super()._kill_process()
|
super()._kill_process()
|
||||||
asyncio.create_task(play(self.ctx))
|
asyncio.run(play(self.ctx))
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# Handles translating urls and search terms
|
# Handles translating urls and search terms
|
||||||
|
|
||||||
import yt_dlp as ytdlp
|
import yt_dlp as ytdlp
|
||||||
|
import spotipy
|
||||||
|
|
||||||
ydl_opts = {
|
ydl_opts = {
|
||||||
'format': 'bestaudio/best',
|
'format': 'bestaudio/best',
|
||||||
@@ -9,7 +10,7 @@ ydl_opts = {
|
|||||||
'ignoreerrors': True,
|
'ignoreerrors': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
def main(url):
|
def main(url, sp):
|
||||||
|
|
||||||
#url = url.lower()
|
#url = url.lower()
|
||||||
|
|
||||||
@@ -20,13 +21,13 @@ def main(url):
|
|||||||
#TODO add better regex or something
|
#TODO add better regex or something
|
||||||
if 'spotify' in url:
|
if 'spotify' in url:
|
||||||
if 'track' in url:
|
if 'track' in url:
|
||||||
return spotify_song(url)
|
return spotify_song(url, sp)
|
||||||
elif 'playlist' in url:
|
elif 'playlist' in url:
|
||||||
return spotify_playlist(url)
|
return spotify_playlist(url)
|
||||||
|
|
||||||
soundcloud_song = 'soundcloud' in url and 'sets' not in url
|
soundcloud_song = 'soundcloud' in url and 'sets' not in url
|
||||||
# Not implemented yet
|
# Not implemented yet
|
||||||
#soundcloud_playlist = 'soundcloud' in url and 'sets' in url
|
# soundcloud_playlist = 'soundcloud' in url and 'sets' in url
|
||||||
|
|
||||||
youtube_song = 'watch?v=' in url or 'youtu.be/' in url
|
youtube_song = 'watch?v=' in url or 'youtu.be/' in url
|
||||||
youtube_playlist = 'playlist?list=' in url
|
youtube_playlist = 'playlist?list=' in url
|
||||||
@@ -57,8 +58,21 @@ def search_song(search):
|
|||||||
return [data]
|
return [data]
|
||||||
|
|
||||||
|
|
||||||
def spotify_song(url):
|
def spotify_song(url, sp):
|
||||||
return []
|
track = sp.track(url.split("/")[-1].split("?")[0])
|
||||||
|
search = ""
|
||||||
|
|
||||||
|
for i in track["artists"]:
|
||||||
|
# grabs all the artists name's if there's more than one
|
||||||
|
search = search + (i['name'] + ", ")
|
||||||
|
|
||||||
|
# remove last comma
|
||||||
|
search = search[:-2]
|
||||||
|
|
||||||
|
# set search to name
|
||||||
|
query = search + " - " + track['name']
|
||||||
|
|
||||||
|
return search_song(query)
|
||||||
|
|
||||||
|
|
||||||
def spotify_playlist(url):
|
def spotify_playlist(url):
|
||||||
@@ -74,8 +88,6 @@ def song_download(url):
|
|||||||
if info is None:
|
if info is None:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
print(info.keys())
|
|
||||||
|
|
||||||
data = {'url': info['url'],
|
data = {'url': info['url'],
|
||||||
'title': info['title'],
|
'title': info['title'],
|
||||||
'thumbnail': info['thumbnail'],
|
'thumbnail': info['thumbnail'],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import discord
|
|||||||
from discord.ext.commands.context import Context
|
from discord.ext.commands.context import Context
|
||||||
from discord.ext.commands.converter import CommandError
|
from discord.ext.commands.converter import CommandError
|
||||||
import config
|
import config
|
||||||
|
from . import queue
|
||||||
|
|
||||||
# Joining/moving to the user's vc in a guild
|
# Joining/moving to the user's vc in a guild
|
||||||
async def join_vc(ctx: Context):
|
async def join_vc(ctx: Context):
|
||||||
@@ -69,7 +69,7 @@ async def display_server_queue(ctx: Context, songs, n):
|
|||||||
title=f"{server.name}'s Queue!",
|
title=f"{server.name}'s Queue!",
|
||||||
color=config.get_color("main"))
|
color=config.get_color("main"))
|
||||||
|
|
||||||
display = ""
|
display = f"🔊 Currently playing: ``{await queue.get_current_song(ctx.guild.id)}``\n"
|
||||||
for i, song in enumerate(songs):
|
for i, song in enumerate(songs):
|
||||||
display += f"``{i + 1}.`` {song[0]} - {format_time(song[1])} Queued by {song[2]}\n"
|
display += f"``{i + 1}.`` {song[0]} - {format_time(song[1])} Queued by {song[2]}\n"
|
||||||
msg.add_field(name="Songs:",
|
msg.add_field(name="Songs:",
|
||||||
|
|||||||
10
config.py
10
config.py
@@ -13,6 +13,16 @@ def read_data():
|
|||||||
raise Exception("Could not load config data")
|
raise Exception("Could not load config data")
|
||||||
|
|
||||||
|
|
||||||
|
def get_spotify_creds():
|
||||||
|
data = read_data()
|
||||||
|
data = data.get("spotify")
|
||||||
|
|
||||||
|
SCID = data.get("SCID")
|
||||||
|
secret = data.get("SECRET")
|
||||||
|
|
||||||
|
return SCID, secret
|
||||||
|
|
||||||
|
|
||||||
# Reading prefix
|
# Reading prefix
|
||||||
def get_prefix():
|
def get_prefix():
|
||||||
data = read_data()
|
data = read_data()
|
||||||
|
|||||||
2
main.py
2
main.py
@@ -6,4 +6,4 @@ import help
|
|||||||
client = Astro(command_prefix=config.get_prefix(), intents=discord.Intents.all())
|
client = Astro(command_prefix=config.get_prefix(), intents=discord.Intents.all())
|
||||||
client.help_command = help.AstroHelp()
|
client.help_command = help.AstroHelp()
|
||||||
|
|
||||||
client.run(config.get_login("live"))
|
client.run(config.get_login("dev"))
|
||||||
@@ -24,4 +24,4 @@ spotipy==2.23.0
|
|||||||
urllib3==2.0.2
|
urllib3==2.0.2
|
||||||
websockets==11.0.3
|
websockets==11.0.3
|
||||||
yarl==1.9.2
|
yarl==1.9.2
|
||||||
yt-dlp==2023.3.4
|
yt-dlp
|
||||||
|
|||||||
Reference in New Issue
Block a user