finished queue system, overwrites discord kill process to loop

This commit is contained in:
2023-05-26 16:49:45 +01:00
parent 8bc45786b1
commit 10907570a2
5 changed files with 174 additions and 71 deletions

View File

@@ -1,20 +1,15 @@
import discord
from discord.ext import commands from discord.ext import commands
from discord.ext.commands.context import Context from discord.ext.commands.context import Context
import cogs.music.util as util 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 datetime import datetime
import pytz import pytz
import yt_dlp
from cogs.music.help import music_help from cogs.music.help import music_help
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': 'downloads/%(title)s.%(ext)s',
}
class music(commands.Cog): class music(commands.Cog):
def __init__(self, client): def __init__(self, client):
@@ -27,6 +22,8 @@ class music(commands.Cog):
help_command.cog = self help_command.cog = self
self.help_command = help_command self.help_command = help_command
queue.initialize_tables()
@commands.command( @commands.command(
help="Displays latency from the bot", help="Displays latency from the bot",
@@ -59,20 +56,26 @@ class music(commands.Cog):
@commands.command( @commands.command(
help="Queues a song into the bot", help="Queues a song into the bot",
aliases=['p', 'qeue', 'q']) aliases=['p', 'qeue', 'q'])
@commands.check(util.in_server)
async def play(self, ctx: Context, *, url=None): async def play(self, ctx: Context, *, url=None):
if url is None: if url is None:
raise commands.CommandError("Must provide a link or search query") raise commands.CommandError("Must provide a link or search query")
server = ctx.guild.id
#TODO potentially save requests before getting stream link
audio = translate.main(url)
#TODO make sure user isn't queuing in dm for some stupid reason
for song in audio:
await queue.add_song(server, song, ctx.author.id)
await ctx.message.add_reaction('👍')
await util.join_vc(ctx) await util.join_vc(ctx)
if await queue.is_server_playing(ctx.guild.id):
return
with yt_dlp.YoutubeDL(ydl_opts) as ydl: await queue.update_server(server, True)
info = ydl.extract_info(url, download=True) await queue.play(ctx)
filename = ydl.prepare_filename(info)
ctx.voice_client.play(discord.FFmpegPCMAudio(executable="ffmpeg", source=filename), after=self.test)
def test(self, error):
print("Hello")

View File

@@ -1,7 +1,17 @@
import sqlite3 import sqlite3
import discord
import asyncio
db_path = "./data/music.db" db_path = "./data/music.db"
FFMPEG_OPTS = {
'before_options':
'-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
'options':
'-vn'
}
# Creates the tables if they don't exist # Creates the tables if they don't exist
def initialize_tables(): def initialize_tables():
@@ -12,18 +22,24 @@ 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
)''') );''')
# Set all to not playing
cursor.execute("UPDATE servers SET is_playing = 0;")
# Create queue table if it doesn't exist # Create queue table if it doesn't exist
cursor.execute('''CREATE TABLE IF NOT EXISTS queue ( cursor.execute('''CREATE TABLE IF NOT EXISTS queue (
server_id TEXT, server_id TEXT NOT NULL,
song_link TEXT, song_link TEXT,
queued_by TEXT, queued_by TEXT,
index INTEGER, position INTEGER NOT NULL,
has_played INTEGER DEFAULT 0, has_played INTEGER DEFAULT 0,
PRIMARY KEY (server_id, order_num) PRIMARY KEY (position),
)''') FOREIGN KEY (server_id) REFERENCES servers(server_id)
);''')
# Clear all entries
cursor.execute("DELETE FROM queue;")
# Commit the changes and close the connection # Commit the changes and close the connection
conn.commit() conn.commit()
@@ -31,26 +47,17 @@ def initialize_tables():
# Queue a song in the db # Queue a song in the db
def add_song(server_id, song_link, queued_by): async def add_song(server_id, song_link, queued_by):
# Connect to db # Connect to db
conn = sqlite3.connect(db_path) conn = sqlite3.connect(db_path)
cursor = conn.cursor() cursor = conn.cursor()
add_server(server_id, cursor, conn) await add_server(server_id, cursor, conn)
# Grab current index max_order_num = await get_max(server_id, cursor) + 1
cursor.execute(f"""
SELECT MAX(index)
FROM queue
WHERE server_id = ?
""", (server_id,))
result = cursor.fetchone()
# Highnest number or 0
max_order_num = result[0] + 1 if result[0] is not None else 0
cursor.execute(""" cursor.execute("""
INSERT INTO queue (server_id, song_link, queued_by, index) INSERT INTO queue (server_id, song_link, queued_by, position)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?)
""", (server_id, song_link, queued_by, max_order_num)) """, (server_id, song_link, queued_by, max_order_num))
@@ -59,7 +66,7 @@ def add_song(server_id, song_link, queued_by):
# Add server to db if first time queuing # Add server to db if first time queuing
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
cursor.execute('''SELECT COUNT(*) cursor.execute('''SELECT COUNT(*)
FROM servers FROM servers
@@ -76,46 +83,73 @@ def add_server(server_id, cursor, conn):
# set song as played and update indexes # set song as played and update indexes
def mark_song_as_finished(server_id, order_num): async def mark_song_as_finished(server_id, order_num):
# Connect to the database # Connect to the database
conn = sqlite3.connect(db_path) conn = sqlite3.connect(db_path)
cursor = conn.cursor() cursor = conn.cursor()
# Update the song as finished # Update the song as finished
cursor.execute('''DELETE FROM queue cursor.execute('''DELETE FROM queue
WHERE server_id = ? AND order_num = ?''', WHERE server_id = ? AND position = ?''',
(server_id, order_num)) (server_id, order_num))
#cursor.execute('''UPDATE queue
# SET is_finished = 1
# WHERE server_id = ? AND index = ?''',
# (server_id, order_num))
# Get the order numbers of unplayed songs
cursor.execute('''SELECT index
FROM queue
WHERE server_id = ? AND is_finished = 0''', (server_id,))
unplayed_order_nums = [row[0] for row in cursor.fetchall()]
# Update the order numbers of unplayed songs
for new_order, old_order in enumerate(unplayed_order_nums, start=1):
cursor.execute('''UPDATE queue
SET order_num = ?
WHERE server_id = ? AND order_num = ?''',
(new_order, server_id, old_order))
# Close connection # Close connection
conn.commit() conn.commit()
conn.close() conn.close()
# Grab max order from server
async def get_max(server_id, cursor):
cursor.execute(f"""
SELECT MAX(position)
FROM queue
WHERE server_id = ?
""", (server_id,))
result = cursor.fetchone()
# Highnest number or 0
max_order_num = result[0] if result[0] is not None else -1
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 queue
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
def update_server(server_id, playing: bool): async def update_server(server_id, playing: bool):
# Connect to database # Connect to database
conn = sqlite3.connect(db_path) conn = sqlite3.connect(db_path)
cursor = conn.cursor() cursor = conn.cursor()
# add server to db if not present # add server to db if not present
add_server(server_id, cursor, conn) await add_server(server_id, cursor, conn)
value = 1 if playing else 0 value = 1 if playing else 0
@@ -129,13 +163,14 @@ def update_server(server_id, playing: bool):
conn.commit() conn.commit()
conn.close() conn.close()
def is_server_playing(server_id):
async def is_server_playing(server_id):
# Connect to db # Connect to db
conn = sqlite3.connect(db_path) conn = sqlite3.connect(db_path)
cursor = conn.cursor() cursor = conn.cursor()
# add server to db if not present # add server to db if not present
add_server(server_id, cursor, conn) await add_server(server_id, cursor, conn)
cursor.execute("""SELECT is_playing cursor.execute("""SELECT is_playing
FROM servers FROM servers
@@ -143,8 +178,55 @@ def is_server_playing(server_id):
(server_id,)) (server_id,))
result = cursor.fetchone() result = cursor.fetchone()
print(result)
conn.commit() conn.commit()
conn.close() conn.close()
return result return True if result[0] == 1 else False
# Delete all songs from a server
async def clear(server_id):
# Connect to db
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
await add_server(server_id, cursor, conn)
await update_server(server_id, False)
# Delete all songs from the server
cursor.execute('''DELETE FROM queue WHERE server_id = ?''', (server_id,))
conn.commit()
conn.close()
# Play and loop songs in server
async def play(ctx):
server_id = ctx.guild.id
# Wait until song is stopped playing
#while ctx.voice_client.is_playing():
#await asyncio.sleep(1)
# check next song
url = await pop(server_id)
# if no other song update server and return
if url is None:
await update_server(server_id, False)
return
# else play next song and call play again
ctx.voice_client.play(
AstroPlayer(ctx, url, FFMPEG_OPTS))
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.run(play(self.ctx))

View File

@@ -1,8 +1,16 @@
# Handles translating urls and search terms # Handles translating urls and search terms
import yt_dlp as ytdlp
ydl_opts = {
'format': 'bestaudio/best',
'quiet': True,
'default_search': 'ytsearch',
}
def main(url): def main(url):
url = url.lower() #url = url.lower()
# Check if link or search # Check if link or search
if url.startswith("https://") is False: if url.startswith("https://") is False:
@@ -16,7 +24,8 @@ def main(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
soundcloud_playlist = 'soundcloud' in url and 'sets' in url # Not implemented yet
#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
@@ -27,24 +36,30 @@ def main(url):
if youtube_playlist: if youtube_playlist:
return playlist_download(url) return playlist_download(url)
return False return []
def search_song(search): def search_song(search):
return None with ytdlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(f"ytsearch1:{search}", download=False)
audio_url = info['entries'][0]['url'] # Get audio stream URL
return [audio_url]
def spotify_song(url): def spotify_song(url):
return None return []
def spotify_playlist(url): def spotify_playlist(url):
return None return []
def song_download(url): def song_download(url):
return None with ytdlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
audio_url = info['url'] # Get audio stream URL
return [audio_url]
def playlist_download(url): def playlist_download(url):
return None return []

View File

@@ -1,5 +1,3 @@
import discord
from discord.ext import commands
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
@@ -45,3 +43,8 @@ async def leave_vc(ctx: Context):
# Disconnect # Disconnect
await ctx.voice_client.disconnect(force=False) await ctx.voice_client.disconnect(force=False)
# Check if command was entered in a server
async def in_server(ctx: Context):
return ctx.guild != None

View File

@@ -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("dev")) client.run(config.get_login("live"))