Compare commits
No commits in common. "316d0ff3f4a0efe7dbc60b94caf1155f6d0fb6d1" and "393403ef7d8946df21121004eeba4a6a7467dee6" have entirely different histories.
316d0ff3f4
...
393403ef7d
@ -17,11 +17,8 @@ class ArgumentParser:
|
||||
self.parser.print_help()
|
||||
return help_buffer.getvalue().replace(" and exit", "")
|
||||
|
||||
def add_mutually_exclusive_group(self, *args, **kwargs):
|
||||
return self.parser.add_mutually_exclusive_group(*args, **kwargs)
|
||||
|
||||
def add_argument(self, *args, **kwargs):
|
||||
return self.parser.add_argument(*args, **kwargs)
|
||||
self.parser.add_argument(*args, **kwargs)
|
||||
|
||||
async def parse_args(self, message, tokens) -> argparse.Namespace | None:
|
||||
try:
|
||||
|
@ -1,7 +1,6 @@
|
||||
import re
|
||||
|
||||
import arguments
|
||||
|
||||
import commands
|
||||
import utils
|
||||
|
||||
|
@ -39,30 +39,30 @@ def match(command: str) -> None | list[Command]:
|
||||
|
||||
def tokenize(string: str) -> list[str]:
|
||||
tokens = []
|
||||
token = ""
|
||||
current_token = []
|
||||
in_quotes = False
|
||||
quote_char = None
|
||||
escape = False
|
||||
escape_next = False
|
||||
|
||||
for char in string[len(constants.PREFIX) :]:
|
||||
if escape:
|
||||
token += char
|
||||
escape = False
|
||||
if escape_next:
|
||||
current_token.append(char)
|
||||
escape_next = False
|
||||
elif char == "\\":
|
||||
escape = True
|
||||
elif char in ('"', "'") and not in_quotes:
|
||||
in_quotes = True
|
||||
quote_char = char
|
||||
elif char == quote_char and in_quotes:
|
||||
escape_next = True
|
||||
elif char in ['"', "'"]:
|
||||
if in_quotes:
|
||||
if current_token and current_token[0] == char:
|
||||
in_quotes = False
|
||||
quote_char = None
|
||||
elif char.isspace() and not in_quotes:
|
||||
if token:
|
||||
tokens.append(token)
|
||||
token = ""
|
||||
else:
|
||||
token += char
|
||||
in_quotes = True
|
||||
elif char.isspace() and not in_quotes:
|
||||
if current_token:
|
||||
tokens.append("".join(current_token))
|
||||
current_token = []
|
||||
else:
|
||||
current_token.append(char)
|
||||
|
||||
if current_token:
|
||||
tokens.append("".join(current_token))
|
||||
|
||||
if token:
|
||||
tokens.append(token)
|
||||
return tokens
|
||||
|
@ -4,7 +4,7 @@ import arguments
|
||||
import commands
|
||||
import utils
|
||||
import youtubedl
|
||||
from state import client, players
|
||||
from state import client, player_current, player_queue
|
||||
|
||||
|
||||
async def queue_or_play(message):
|
||||
@ -12,16 +12,21 @@ async def queue_or_play(message):
|
||||
if not command_allowed(message):
|
||||
return
|
||||
|
||||
if message.guild.id not in players:
|
||||
players[message.guild.id] = youtubedl.QueuedPlayer()
|
||||
if message.guild.id not in player_queue:
|
||||
player_queue[message.guild.id] = []
|
||||
|
||||
tokens = commands.tokenize(message.content)
|
||||
parser = arguments.ArgumentParser(
|
||||
tokens[0], "queue a song, list the queue, or resume playback"
|
||||
)
|
||||
parser.add_argument("query", nargs="?", help="yt-dlp URL or query to get song")
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--clear",
|
||||
action="store_true",
|
||||
help="clear all queued songs",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--volume",
|
||||
default=50,
|
||||
@ -29,68 +34,13 @@ async def queue_or_play(message):
|
||||
metavar="[0-150]",
|
||||
help="the volume level (0 - 150)",
|
||||
)
|
||||
group.add_argument(
|
||||
"-i",
|
||||
"--remove-index",
|
||||
type=int,
|
||||
help="remove a queued song by index",
|
||||
)
|
||||
group.add_argument(
|
||||
"-m",
|
||||
"--remove-multiple",
|
||||
action="store_true",
|
||||
help="continue removing queued songs after finding a match",
|
||||
)
|
||||
group.add_argument(
|
||||
"-c",
|
||||
"--clear",
|
||||
action="store_true",
|
||||
help="remove all queued songs",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--remove-title",
|
||||
help="remove queued songs by title",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q",
|
||||
"--remove-queuer",
|
||||
type=int,
|
||||
help="remove queued songs by queuer",
|
||||
)
|
||||
if not (args := await parser.parse_args(message, tokens)):
|
||||
return
|
||||
|
||||
if args.clear:
|
||||
players[message.guild.id].queue.clear()
|
||||
player_queue[message.guild.id] = []
|
||||
await utils.add_check_reaction(message)
|
||||
return
|
||||
elif i := args.remove_index:
|
||||
try:
|
||||
queued = players[message.guild.id].queue[i - 1]
|
||||
del players[message.guild.id].queue[i - 1]
|
||||
await utils.reply(message, f"**x** {queued.format()}")
|
||||
except:
|
||||
await utils.reply(message, "invalid index!")
|
||||
elif args.remove_title or args.remove_queuer:
|
||||
targets = []
|
||||
for queued in players[message.guild.id].queue:
|
||||
if t := args.remove_title:
|
||||
if t in queued.player.title:
|
||||
targets.append(queued)
|
||||
continue
|
||||
if q := args.remove_queuer:
|
||||
if q == queued.queuer:
|
||||
targets.append(queued)
|
||||
if args.remove_multiple:
|
||||
targets = targets[:1]
|
||||
|
||||
for target in targets:
|
||||
players[message.guild.id].queue.remove(target)
|
||||
await utils.reply(
|
||||
message,
|
||||
f"removed **{len(targets)}** queued {'song' if len(targets) == 1 else 'songs'}",
|
||||
)
|
||||
elif query := args.query:
|
||||
try:
|
||||
async with message.channel.typing():
|
||||
@ -105,21 +55,23 @@ async def queue_or_play(message):
|
||||
)
|
||||
return
|
||||
|
||||
queued = youtubedl.QueuedSong(player, message.author.id)
|
||||
players[message.guild.id].queue_add(queued)
|
||||
player_queue[message.guild.id].insert(
|
||||
0, {"player": player, "queuer": message.author.id}
|
||||
)
|
||||
|
||||
if (
|
||||
not message.guild.voice_client.is_playing()
|
||||
and not message.guild.voice_client.is_paused()
|
||||
):
|
||||
await utils.reply(message, f"**0.** {queued.format()}")
|
||||
await utils.reply(message, f"**now playing:** `{player.title}`")
|
||||
play_next(message)
|
||||
else:
|
||||
await utils.reply(
|
||||
message,
|
||||
f"**+** {queued.format()}",
|
||||
f"**+** `{player.title}`",
|
||||
)
|
||||
else:
|
||||
if message.guild.voice_client:
|
||||
if tokens[0].lower() == "play":
|
||||
message.guild.voice_client.resume()
|
||||
await utils.reply(
|
||||
@ -127,34 +79,35 @@ async def queue_or_play(message):
|
||||
"resumed!",
|
||||
)
|
||||
else:
|
||||
currently_playing = (
|
||||
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)
|
||||
]
|
||||
generate_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']}>)"
|
||||
)
|
||||
if (
|
||||
not players[message.guild.id].queue
|
||||
not player_queue[message.guild.id]
|
||||
and not message.guild.voice_client.source
|
||||
):
|
||||
await utils.reply(
|
||||
message,
|
||||
"nothing is playing or queued!",
|
||||
)
|
||||
elif not players[message.guild.id].queue:
|
||||
await utils.reply(message, currently_playing())
|
||||
elif not player_queue[message.guild.id]:
|
||||
await utils.reply(message, generate_currently_playing())
|
||||
elif not message.guild.voice_client.source:
|
||||
await utils.reply(
|
||||
message,
|
||||
queue_list(),
|
||||
generate_queue_list(player_queue[message.guild.id]),
|
||||
)
|
||||
else:
|
||||
await utils.reply(
|
||||
message,
|
||||
currently_playing() + "\n" + queue_list(),
|
||||
generate_currently_playing()
|
||||
+ "\n"
|
||||
+ generate_queue_list(player_queue[message.guild.id]),
|
||||
)
|
||||
else:
|
||||
await utils.reply(
|
||||
message,
|
||||
"nothing is currently queued!",
|
||||
)
|
||||
|
||||
|
||||
@ -162,7 +115,7 @@ async def skip(message):
|
||||
if not command_allowed(message):
|
||||
return
|
||||
|
||||
if not players[message.guild.id].queue:
|
||||
if not player_queue[message.guild.id]:
|
||||
message.guild.voice_client.stop()
|
||||
await utils.reply(
|
||||
message,
|
||||
@ -191,13 +144,10 @@ async def resume(message):
|
||||
if not command_allowed(message):
|
||||
return
|
||||
|
||||
if message.guild.voice_client.is_paused():
|
||||
message.guild.voice_client.resume()
|
||||
await utils.add_check_reaction(message)
|
||||
else:
|
||||
await utils.reply(
|
||||
message,
|
||||
"nothing is paused!",
|
||||
"resumed!",
|
||||
)
|
||||
|
||||
|
||||
@ -205,13 +155,10 @@ async def pause(message):
|
||||
if not command_allowed(message):
|
||||
return
|
||||
|
||||
if message.guild.voice_client.is_playing():
|
||||
message.guild.voice_client.pause()
|
||||
await utils.add_check_reaction(message)
|
||||
else:
|
||||
await utils.reply(
|
||||
message,
|
||||
"nothing is playing!",
|
||||
"paused!",
|
||||
)
|
||||
|
||||
|
||||
@ -237,7 +184,7 @@ async def volume(message):
|
||||
if not message.guild.voice_client.source:
|
||||
await utils.reply(
|
||||
message,
|
||||
f"nothing is playing!",
|
||||
f"there is no player currently active!",
|
||||
)
|
||||
return
|
||||
|
||||
@ -251,19 +198,13 @@ async def volume(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):
|
||||
message.guild.voice_client.stop()
|
||||
if players[message.guild.id].queue:
|
||||
queued = players[message.guild.id].queue_pop()
|
||||
if player_queue[message.guild.id]:
|
||||
queued = player_queue[message.guild.id].pop()
|
||||
player_current[message.guild.id] = queued
|
||||
message.guild.voice_client.play(
|
||||
queued.player, after=lambda e: play_after_callback(e, message, once)
|
||||
queued["player"], after=lambda _: play_next(message) if not once else None
|
||||
)
|
||||
|
||||
|
||||
@ -279,3 +220,12 @@ def command_allowed(message):
|
||||
if not message.guild.voice_client:
|
||||
return False
|
||||
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)
|
||||
]
|
||||
)
|
||||
|
@ -13,7 +13,6 @@ RELOADABLE_MODULES = [
|
||||
"constants",
|
||||
"core",
|
||||
"events",
|
||||
"player",
|
||||
"utils",
|
||||
"voice",
|
||||
"youtubedl",
|
||||
|
5
core.py
5
core.py
@ -30,9 +30,6 @@ async def on_message(message):
|
||||
)
|
||||
return
|
||||
|
||||
if message.guild.id not in command_locks:
|
||||
command_locks[message.guild.id] = asyncio.Lock()
|
||||
|
||||
C = commands.Command
|
||||
try:
|
||||
match matched[0]:
|
||||
@ -98,6 +95,8 @@ async def on_message(message):
|
||||
case C.LEAVE:
|
||||
await commands.voice.leave(message)
|
||||
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]:
|
||||
await commands.voice.queue_or_play(message)
|
||||
case C.SKIP:
|
||||
|
3
state.py
3
state.py
@ -2,7 +2,8 @@ import time
|
||||
|
||||
import disnake
|
||||
|
||||
players = {}
|
||||
player_queue = {}
|
||||
player_current = {}
|
||||
command_locks = {}
|
||||
|
||||
intents = disnake.Intents.default()
|
||||
|
29
youtubedl.py
29
youtubedl.py
@ -14,9 +14,7 @@ class YTDLSource(disnake.PCMVolumeTransformer):
|
||||
self, source: disnake.AudioSource, *, data: dict[str, Any], volume: float = 0.5
|
||||
):
|
||||
super().__init__(source, volume)
|
||||
print(data)
|
||||
self.title = data.get("title")
|
||||
self.original_url = data.get("original_url")
|
||||
|
||||
@classmethod
|
||||
async def from_url(
|
||||
@ -43,33 +41,6 @@ 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__():
|
||||
global ytdl
|
||||
ytdl = yt_dlp.YoutubeDL(constants.YTDL_OPTIONS)
|
||||
|
Loading…
x
Reference in New Issue
Block a user