From 77e2175ab610c576fda792f02de04bdbe93087aa Mon Sep 17 00:00:00 2001 From: Top1055 <123alexfeetham@gmail.com> Date: Fri, 10 Nov 2023 02:09:55 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A0=20fixed=20queue=20bugs,=20added=20?= =?UTF-8?q?spotify=20song=20support=20=F0=9F=94=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cogs/music/main.py | 72 +++++++++++++++++++--------- cogs/music/queue.py | 101 +++++++++++++++++++++++++++------------- cogs/music/translate.py | 26 ++++++++--- cogs/music/util.py | 4 +- config.py | 10 ++++ help.py | 4 +- main.py | 2 +- requirements.txt | 2 +- 8 files changed, 155 insertions(+), 66 deletions(-) diff --git a/cogs/music/main.py b/cogs/music/main.py index fc9dfca..eb246fe 100644 --- a/cogs/music/main.py +++ b/cogs/music/main.py @@ -5,12 +5,49 @@ import cogs.music.util as util import cogs.music.queue as queue import cogs.music.translate as translate -import datetime -import pytz -import asyncio 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): def __init__(self, client): @@ -23,21 +60,16 @@ class music(commands.Cog): help_command.cog = self 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() - @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( help="Connects to your current voice channel", aliases=['connect']) @@ -53,7 +85,6 @@ class music(commands.Cog): await util.leave_vc(ctx) await ctx.message.add_reaction('👍') - @commands.command( help="Queues a song into the bot", aliases=['p']) @@ -65,14 +96,14 @@ class music(commands.Cog): server = ctx.guild.id - await ctx.message.add_reaction('👍') await util.join_vc(ctx) + await ctx.message.add_reaction('👍') msg = await ctx.send("Fetching song(s)...") async with ctx.typing(): #TODO potentially save requests before getting stream link # Grab video details such as title thumbnail duration - audio = translate.main(url) + audio = translate.main(url, self.sp) await msg.delete() @@ -99,7 +130,6 @@ class music(commands.Cog): await queue.play(ctx) - @commands.command( help="Display the current music queue", aliases=['q', 'songs']) @@ -116,7 +146,7 @@ class music(commands.Cog): n, songs = await queue.grab_songs(server.id) # 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!") return @@ -148,4 +178,4 @@ class music(commands.Cog): await queue.pop(server.id) # Safe to ignore error for now - ctx.voice_client.stop() + ctx.voice_client.stop() \ No newline at end of file diff --git a/cogs/music/queue.py b/cogs/music/queue.py index 28c030d..8cbfeca 100644 --- a/cogs/music/queue.py +++ b/cogs/music/queue.py @@ -1,3 +1,4 @@ +from http import server import sqlite3 import discord @@ -22,7 +23,8 @@ def initialize_tables(): # Create servers table if it doesn't exist cursor.execute('''CREATE TABLE IF NOT EXISTS servers ( server_id TEXT PRIMARY KEY, - is_playing INTEGER DEFAULT 0 + is_playing INTEGER DEFAULT 0, + song_name TEXT );''') # Set all to not playing @@ -83,6 +85,34 @@ async def add_song(server_id, details, queued_by): 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 async def add_server(server_id, cursor, conn): # Check if the server exists @@ -116,6 +146,42 @@ async def mark_song_as_finished(server_id, order_num): 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 async def get_max(server_id, cursor): cursor.execute(f""" @@ -131,35 +197,6 @@ async def get_max(server_id, cursor): 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 async def update_server(server_id, playing: bool): # Connect to database @@ -259,7 +296,7 @@ async def play(ctx): return # else play next song and call play again - ctx.voice_client.play( + await ctx.voice_client.play( AstroPlayer(ctx, url, FFMPEG_OPTS)) # call play on ffmpeg exit @@ -270,4 +307,4 @@ class AstroPlayer(discord.FFmpegPCMAudio): def _kill_process(self): super()._kill_process() - asyncio.create_task(play(self.ctx)) + asyncio.run(play(self.ctx)) \ No newline at end of file diff --git a/cogs/music/translate.py b/cogs/music/translate.py index 2773b7b..182ed79 100644 --- a/cogs/music/translate.py +++ b/cogs/music/translate.py @@ -1,6 +1,7 @@ # Handles translating urls and search terms import yt_dlp as ytdlp +import spotipy ydl_opts = { 'format': 'bestaudio/best', @@ -9,7 +10,7 @@ ydl_opts = { 'ignoreerrors': True, } -def main(url): +def main(url, sp): #url = url.lower() @@ -20,13 +21,13 @@ def main(url): #TODO add better regex or something if 'spotify' in url: if 'track' in url: - return spotify_song(url) + return spotify_song(url, sp) elif 'playlist' in url: return spotify_playlist(url) soundcloud_song = 'soundcloud' in url and 'sets' not in url # 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_playlist = 'playlist?list=' in url @@ -57,8 +58,21 @@ def search_song(search): return [data] -def spotify_song(url): - return [] +def spotify_song(url, sp): + 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): @@ -74,8 +88,6 @@ def song_download(url): if info is None: return [] - print(info.keys()) - data = {'url': info['url'], 'title': info['title'], 'thumbnail': info['thumbnail'], diff --git a/cogs/music/util.py b/cogs/music/util.py index ffe5fee..80600f2 100644 --- a/cogs/music/util.py +++ b/cogs/music/util.py @@ -2,7 +2,7 @@ import discord from discord.ext.commands.context import Context from discord.ext.commands.converter import CommandError import config - +from . import queue # Joining/moving to the user's vc in a guild 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!", 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): display += f"``{i + 1}.`` {song[0]} - {format_time(song[1])} Queued by {song[2]}\n" msg.add_field(name="Songs:", diff --git a/config.py b/config.py index 32e87b7..02422ee 100644 --- a/config.py +++ b/config.py @@ -13,6 +13,16 @@ def read_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 def get_prefix(): data = read_data() diff --git a/help.py b/help.py index 56a42b0..51f9537 100644 --- a/help.py +++ b/help.py @@ -32,7 +32,7 @@ class AstroHelp(commands.MinimalHelpCommand): # grabs iterable of (Cog, list[Command]) for cog, commands in mapping.items(): - + # Grab commands only the user can access # Safe to ignore warning filtered = await self.filter_commands(commands, sort=True) @@ -78,4 +78,4 @@ class AstroHelp(commands.MinimalHelpCommand): # TODO add error support see # https://gist.github.com/InterStella0/b78488fb28cadf279dfd3164b9f0cf96 # and -# https://gist.github.com/EvieePy/7822af90858ef65012ea500bcecf1612 +# https://gist.github.com/EvieePy/7822af90858ef65012ea500bcecf1612 \ No newline at end of file diff --git a/main.py b/main.py index ebc0868..68a512c 100644 --- a/main.py +++ b/main.py @@ -6,4 +6,4 @@ import help client = Astro(command_prefix=config.get_prefix(), intents=discord.Intents.all()) client.help_command = help.AstroHelp() -client.run(config.get_login("live")) +client.run(config.get_login("dev")) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index fea0871..cf613e7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,4 +24,4 @@ spotipy==2.23.0 urllib3==2.0.2 websockets==11.0.3 yarl==1.9.2 -yt-dlp==2023.3.4 +yt-dlp