refactor: make players and songs object-oriented

This commit is contained in:
Ryan 2024-12-30 19:52:20 -05:00
parent db355e8ade
commit 316d0ff3f4
Signed by: ErrorNoInternet
GPG Key ID: 2486BFB7B1E6A4A3
5 changed files with 113 additions and 83 deletions

View File

@ -4,7 +4,7 @@ import arguments
import commands import commands
import utils import utils
import youtubedl import youtubedl
from state import client, player_current, player_queue from state import client, players
async def queue_or_play(message): async def queue_or_play(message):
@ -12,8 +12,8 @@ async def queue_or_play(message):
if not command_allowed(message): if not command_allowed(message):
return return
if message.guild.id not in player_queue: if message.guild.id not in players:
player_queue[message.guild.id] = [] players[message.guild.id] = youtubedl.QueuedPlayer()
tokens = commands.tokenize(message.content) tokens = commands.tokenize(message.content)
parser = arguments.ArgumentParser( parser = arguments.ArgumentParser(
@ -62,30 +62,31 @@ async def queue_or_play(message):
return return
if args.clear: if args.clear:
player_queue[message.guild.id] = [] players[message.guild.id].queue.clear()
await utils.add_check_reaction(message) await utils.add_check_reaction(message)
return return
elif i := args.remove_index: elif i := args.remove_index:
try: try:
queued = player_queue[message.guild.id][i - 1] queued = players[message.guild.id].queue[i - 1]
del player_queue[message.guild.id][i - 1] del players[message.guild.id].queue[i - 1]
await utils.reply(message, f"**x** `{queued['player'].title}`") await utils.reply(message, f"**x** {queued.format()}")
except: except:
await utils.reply(message, "invalid index!") await utils.reply(message, "invalid index!")
elif args.remove_title or args.remove_queuer: elif args.remove_title or args.remove_queuer:
targets = [] targets = []
for queued in player_queue[message.guild.id]: for queued in players[message.guild.id].queue:
if t := args.remove_title: if t := args.remove_title:
if t in queued["player"].title: if t in queued.player.title:
targets.append(queued) targets.append(queued)
continue
if q := args.remove_queuer: if q := args.remove_queuer:
if q == queued["queuer"]: if q == queued.queuer:
targets.append(queued) targets.append(queued)
if not args.remove_multiple: if args.remove_multiple:
targets = targets[:1] targets = targets[:1]
for target in targets: for target in targets:
if target in player_queue[message.guild.id]: players[message.guild.id].queue.remove(target)
player_queue[message.guild.id].remove(target)
await utils.reply( await utils.reply(
message, message,
f"removed **{len(targets)}** queued {'song' if len(targets) == 1 else 'songs'}", f"removed **{len(targets)}** queued {'song' if len(targets) == 1 else 'songs'}",
@ -104,23 +105,21 @@ async def queue_or_play(message):
) )
return return
player_queue[message.guild.id].insert( queued = youtubedl.QueuedSong(player, message.author.id)
0, {"player": player, "queuer": message.author.id} players[message.guild.id].queue_add(queued)
)
if ( if (
not message.guild.voice_client.is_playing() not message.guild.voice_client.is_playing()
and not message.guild.voice_client.is_paused() and not message.guild.voice_client.is_paused()
): ):
await utils.reply(message, f"**0.** `{player.title}`") await utils.reply(message, f"**0.** {queued.format()}")
play_next(message) play_next(message)
else: else:
await utils.reply( await utils.reply(
message, message,
f"**+** `{player.title}`", f"**+** {queued.format()}",
) )
else: else:
if message.guild.voice_client:
if tokens[0].lower() == "play": if tokens[0].lower() == "play":
message.guild.voice_client.resume() message.guild.voice_client.resume()
await utils.reply( await utils.reply(
@ -128,35 +127,34 @@ async def queue_or_play(message):
"resumed!", "resumed!",
) )
else: else:
generate_currently_playing = ( currently_playing = (
lambda: f"**0.** {'(paused) ' if message.guild.voice_client.is_paused() else ''}`{message.guild.voice_client.source.title}` (<@{player_current[message.guild.id]['queuer']}>)" lambda: f"**0.** {'(paused) ' if message.guild.voice_client.is_paused() else ''} {players[message.guild.id].current.format(with_queuer=True)}"
)
queue_list = lambda: "\n".join(
[
f"**{i + 1}.** {queued.format(with_queuer=True, hide_preview=True)}"
for i, queued in enumerate(players[message.guild.id].queue)
]
) )
if ( if (
not player_queue[message.guild.id] not players[message.guild.id].queue
and not message.guild.voice_client.source and not message.guild.voice_client.source
): ):
await utils.reply( await utils.reply(
message, message,
"nothing is playing or queued!", "nothing is playing or queued!",
) )
elif not player_queue[message.guild.id]: elif not players[message.guild.id].queue:
await utils.reply(message, generate_currently_playing()) await utils.reply(message, currently_playing())
elif not message.guild.voice_client.source: elif not message.guild.voice_client.source:
await utils.reply( await utils.reply(
message, message,
generate_queue_list(player_queue[message.guild.id]), queue_list(),
) )
else: else:
await utils.reply( await utils.reply(
message, message,
generate_currently_playing() currently_playing() + "\n" + queue_list(),
+ "\n"
+ generate_queue_list(player_queue[message.guild.id]),
)
else:
await utils.reply(
message,
"nothing is currently queued!",
) )
@ -164,7 +162,7 @@ async def skip(message):
if not command_allowed(message): if not command_allowed(message):
return return
if not player_queue[message.guild.id]: if not players[message.guild.id].queue:
message.guild.voice_client.stop() message.guild.voice_client.stop()
await utils.reply( await utils.reply(
message, message,
@ -193,10 +191,13 @@ async def resume(message):
if not command_allowed(message): if not command_allowed(message):
return return
if message.guild.voice_client.is_paused():
message.guild.voice_client.resume() message.guild.voice_client.resume()
await utils.add_check_reaction(message)
else:
await utils.reply( await utils.reply(
message, message,
"resumed!", "nothing is paused!",
) )
@ -204,10 +205,13 @@ async def pause(message):
if not command_allowed(message): if not command_allowed(message):
return return
if message.guild.voice_client.is_playing():
message.guild.voice_client.pause() message.guild.voice_client.pause()
await utils.add_check_reaction(message)
else:
await utils.reply( await utils.reply(
message, message,
"paused!", "nothing is playing!",
) )
@ -233,7 +237,7 @@ async def volume(message):
if not message.guild.voice_client.source: if not message.guild.voice_client.source:
await utils.reply( await utils.reply(
message, message,
f"there is no player currently active!", f"nothing is playing!",
) )
return return
@ -247,14 +251,19 @@ async def volume(message):
await utils.add_check_reaction(message) await utils.add_check_reaction(message)
def play_after_callback(e, message, once):
if e:
print(f"player error: {e}")
if not once:
play_next(message)
def play_next(message, once=False): def play_next(message, once=False):
message.guild.voice_client.stop() message.guild.voice_client.stop()
if player_queue[message.guild.id]: if players[message.guild.id].queue:
queued = player_queue[message.guild.id][0] queued = players[message.guild.id].queue_pop()
del player_queue[message.guild.id][0]
player_current[message.guild.id] = queued
message.guild.voice_client.play( message.guild.voice_client.play(
queued["player"], after=lambda _: play_next(message) if not once else None queued.player, after=lambda e: play_after_callback(e, message, once)
) )
@ -270,12 +279,3 @@ def command_allowed(message):
if not message.guild.voice_client: if not message.guild.voice_client:
return False return False
return message.author.voice.channel.id == message.guild.voice_client.channel.id return message.author.voice.channel.id == message.guild.voice_client.channel.id
def generate_queue_list(queue: list):
return "\n".join(
[
f"**{i + 1}.** `{queued['player'].title}` (<@{queued['queuer']}>)"
for i, queued in enumerate(queue)
]
)

View File

@ -13,6 +13,7 @@ RELOADABLE_MODULES = [
"constants", "constants",
"core", "core",
"events", "events",
"player",
"utils", "utils",
"voice", "voice",
"youtubedl", "youtubedl",

View File

@ -30,6 +30,9 @@ async def on_message(message):
) )
return return
if message.guild.id not in command_locks:
command_locks[message.guild.id] = asyncio.Lock()
C = commands.Command C = commands.Command
try: try:
match matched[0]: match matched[0]:
@ -95,8 +98,6 @@ async def on_message(message):
case C.LEAVE: case C.LEAVE:
await commands.voice.leave(message) await commands.voice.leave(message)
case C.QUEUE | C.PLAY: case C.QUEUE | C.PLAY:
if message.guild.id not in command_locks:
command_locks[message.guild.id] = asyncio.Lock()
async with command_locks[message.guild.id]: async with command_locks[message.guild.id]:
await commands.voice.queue_or_play(message) await commands.voice.queue_or_play(message)
case C.SKIP: case C.SKIP:

View File

@ -2,8 +2,7 @@ import time
import disnake import disnake
player_queue = {} players = {}
player_current = {}
command_locks = {} command_locks = {}
intents = disnake.Intents.default() intents = disnake.Intents.default()

View File

@ -14,7 +14,9 @@ class YTDLSource(disnake.PCMVolumeTransformer):
self, source: disnake.AudioSource, *, data: dict[str, Any], volume: float = 0.5 self, source: disnake.AudioSource, *, data: dict[str, Any], volume: float = 0.5
): ):
super().__init__(source, volume) super().__init__(source, volume)
print(data)
self.title = data.get("title") self.title = data.get("title")
self.original_url = data.get("original_url")
@classmethod @classmethod
async def from_url( async def from_url(
@ -41,6 +43,33 @@ class YTDLSource(disnake.PCMVolumeTransformer):
) )
class QueuedPlayer:
def __init__(self):
self.queue = []
self.current = None
def queue_pop(self):
popped = self.queue[0]
del self.queue[0]
self.current = popped
return popped
def queue_add(self, item):
self.queue.append(item)
class QueuedSong:
def __init__(self, player, queuer):
self.player = player
self.queuer = queuer
def format(self, with_queuer=False, hide_preview=False):
return (
f"[`{self.player.title}`]({'<' if hide_preview else ''}{self.player.original_url}{'>' if hide_preview else ''})"
+ (f" (<@{self.queuer}>)" if with_queuer else "")
)
def __reload_module__(): def __reload_module__():
global ytdl global ytdl
ytdl = yt_dlp.YoutubeDL(constants.YTDL_OPTIONS) ytdl = yt_dlp.YoutubeDL(constants.YTDL_OPTIONS)