Merge pull request #48 from r-Norge/feature/more_type_hints

Add more typing information. Split user queue with index into separate functions
This commit is contained in:
Ev-1 authored and GitHub committed 2022-10-04 23:04:10 +02:00
commit 4865f43827
6 files changed
+83 -70

No files matched your search

+19 -14
View File
@@ -4,11 +4,13 @@ from discord.ext import commands, tasks
from lavalink.events import (
NodeChangedEvent, NodeConnectedEvent, NodeDisconnectedEvent, PlayerUpdateEvent, QueueEndEvent, TrackEndEvent,
TrackStartEvent, TrackStuckEvent)
from lavalink.models import AudioTrack
import asyncio
import json
import re
import urllib
from typing import List
from bs4 import BeautifulSoup
@@ -32,8 +34,7 @@ class Music(commands.Cog):
self.bot = bot
self.leave_timer.start()
self.logger = self.bot.main_logger.bot_logger.getChild("Errors")
bot.lavalink.add_event_hook(self.track_hook)
self.bot.lavalink.add_event_hook(self.track_hook)
async def cog_check(self, ctx):
if not ctx.guild:
@@ -279,12 +280,12 @@ class Music(commands.Cog):
player: MixPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id)
# We create a new selector for each selection
def move_selector(ctx, queue, title, first: bool):
def move_selector(ctx, queue: List[AudioTrack], title: str, first: bool):
async def return_track(track):
return track
choices = []
for index, (track, _) in enumerate(queue, start=1):
for index, track in enumerate(queue, start=1):
blank = " " * 6
if first:
prefix = f'`{blank}`\n`{index:<6}`'
@@ -301,7 +302,7 @@ class Music(commands.Cog):
page = 0
while True:
# Prompt the user for which track to move
selector = move_selector(ctx, player.user_queue(ctx.author.id, dual=True), "{moved.choose_pos}", True)
selector = move_selector(ctx, player.user_queue(ctx.author.id), "{moved.choose_pos}", True)
message, track_to_move = await selector.start_scrolling(ClearOn.Timeout, message, page)
page = selector.page_number
@@ -309,7 +310,7 @@ class Music(commands.Cog):
break
# Prompt the user for where to move it
selector = move_selector(ctx, player.user_queue(ctx.author.id, dual=True), "{moved.choose_song}", False)
selector = move_selector(ctx, player.user_queue(ctx.author.id), "{moved.choose_song}", False)
message, track_to_replace = await selector.start_scrolling(ClearOn.Timeout, message, page)
page = selector.page_number
@@ -320,11 +321,10 @@ class Music(commands.Cog):
# one in the queue. Find the index of both songs in the current queue
# Keep updating the user queue in case it changes while the user is selecting songs
user_queue = player.user_queue(ctx.author.id, dual=True)
user_queue_identifiers = [track for (track, _) in user_queue]
user_queue = player.user_queue(ctx.author.id)
try:
pos_initial = user_queue_identifiers.index(track_to_move[0])
pos_final = user_queue_identifiers.index(track_to_replace[0])
pos_initial = user_queue.index(track_to_move[0])
pos_final = user_queue.index(track_to_replace[0])
player.move_user_track(ctx.author.id, pos_initial, pos_final)
except ValueError:
# Track not found in queue. Queue could have changed during selection
@@ -340,7 +340,7 @@ class Music(commands.Cog):
async def _remove(self, ctx):
player: MixPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id)
user_queue = player.user_queue(ctx.author.id, dual=True)
user_queue: List[AudioTrack] = player.user_queue(ctx.author.id)
tracks_to_remove = []
@@ -363,7 +363,7 @@ class Music(commands.Cog):
return inner
selector_buttons = []
for index, (track, _) in enumerate(user_queue, start=1):
for index, track in enumerate(user_queue, start=1):
selector_buttons.append(
SelectorItem(f'`{index}` [{track.title}]({track.uri})', str(index),
add_change_button_color(update_remove_list, tracks_to_remove, track)))
@@ -377,13 +377,18 @@ class Music(commands.Cog):
paginator = TextPaginator(color=ctx.me.color, title="Removed")
# If the user spent a long time selecting songs the queue might have changed
user_queue = player.user_queue(ctx.author.id, dual=False)
user_queue = player.user_queue(ctx.author.id)
track_indexes_to_remove = [i for i, track in enumerate(user_queue) if track in tracks_to_remove]
# Sort in reverse order since songs shift index when deleting
tracks_removed = []
for track in sorted(track_indexes_to_remove, reverse=True):
if removed_track := player.remove_user_track(ctx.author.id, track):
paginator.add_line(f"{removed_track.title}")
tracks_removed.append(f"{removed_track.title}")
# We want to display the removed tracks in the original queue order, so reverse the list once again
for removed in reversed(tracks_removed):
paginator.add_line(removed)
paginator.close_page()
scroller = Scroller(ctx, paginator)
+7 -7
View File
@@ -2,7 +2,7 @@ import logging
from collections import OrderedDict, deque
from itertools import chain, cycle, islice
from random import shuffle
from typing import Generic, Iterable, Iterator, List, Tuple, TypeVar, Union
from typing import Generic, Iterable, Iterator, List, Optional, Tuple, TypeVar
# Would like to ensure the T has a "requester" attribute, but don't know if that is possible
T = TypeVar('T')
@@ -80,7 +80,7 @@ class MixQueue(Generic[T]):
combined = zip(queue, pos)
return list(combined)
def pop_first(self) -> Union[T, None]:
def pop_first(self) -> Optional[T]:
if self.priority_queue:
next_track = self.priority_queue.pop(0)
self._history.append(next_track)
@@ -110,7 +110,7 @@ class MixQueue(Generic[T]):
except KeyError:
pass
def add_track(self, requester: int, track: T, pos: Union[int, None] = None) -> Tuple[T, int, int]:
def add_track(self, requester: int, track: T, pos: Optional[int] = None) -> Tuple[T, int, int]:
user_queue = self.queues.get(requester)
if user_queue is None:
self.queues[requester] = [track]
@@ -138,7 +138,7 @@ class MixQueue(Generic[T]):
else:
self.queues.pop(requester)
def remove_user_track(self, requester: int, pos: int) -> Union[None, T]:
def remove_user_track(self, requester: int, pos: int) -> Optional[T]:
user_queue = self.queues.get(requester)
if user_queue is not None:
if pos < len(user_queue):
@@ -149,7 +149,7 @@ class MixQueue(Generic[T]):
self._clear_empty()
return track
def remove_global_track(self, pos: int) -> Union[T, None]:
def remove_global_track(self, pos: int) -> Optional[T]:
# Get the actual index of the song
index = list(self).index(self.get_queue()[pos])
@@ -217,7 +217,7 @@ class MixQueue(Generic[T]):
globpos += 1
return globpos
def _glob_to_loc(self, pos: int) -> Tuple[Union[T, None], Union[int, None]]:
def _glob_to_loc(self, pos: int) -> Tuple[Optional[T], Optional[int]]:
try:
song = next(islice(self, pos, pos + 1))
except (ValueError, StopIteration):
@@ -234,7 +234,7 @@ class MixQueue(Generic[T]):
return None, None
@property
def first_queue(self) -> Union[int, None]:
def first_queue(self) -> Optional[int]:
try:
return list(self.queues.keys())[0]
except IndexError:
+30 -28
View File
@@ -5,8 +5,7 @@ from lavalink import AudioTrack, DefaultPlayer, Node
from lavalink.events import QueueEndEvent, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent
from lavalink.filters import Equalizer, Timescale
import typing
from typing import Union
from typing import Dict, List, Optional, Set, Tuple
from .mixqueue import MixQueue
@@ -15,13 +14,13 @@ class MixPlayer(DefaultPlayer):
def __init__(self, guild_id: int, node: Node):
super().__init__(guild_id, node)
self.queue: MixQueue = MixQueue()
self.queue: MixQueue[AudioTrack] = MixQueue()
self.listeners = set()
self.voteables = {}
self.skip_voters = set()
self.boosted = False
self.nightcore_enabled = False
self.listeners: Set[int] = set()
self.voteables: Dict[int, Set[int]] = {}
self.skip_voters: Set[int] = set()
self.boosted: bool = False
self.nightcore_enabled: bool = False
bands = [
(0, 0.15),
(1, 0.15),
@@ -31,17 +30,17 @@ class MixPlayer(DefaultPlayer):
(5, -0.1),
(6, -0.05)
]
self.bass_boost_filter = Equalizer()
self.bass_boost_filter: lavalink.Filter = Equalizer()
self.bass_boost_filter.update(bands=bands)
self.nightcore_filter = Timescale()
self.nightcore_filter: lavalink.Filter = Timescale()
self.nightcore_filter.update(speed=1.25, pitch=1.25)
def add(self, requester: int, track: typing.Union[dict, AudioTrack], pos: int = None):
def add(self, requester: int, track: AudioTrack, pos: Optional[int] = None) -> Tuple[AudioTrack, int, int]:
""" Adds a track to the queue. """
return self.queue.add_track(requester, track, pos)
def add_next(self, requester: int, track: typing.Union[dict, AudioTrack], pos: int = None):
def add_priority(self, track: AudioTrack):
""" Adds a track to beginning of the queue """
self.queue.add_next_track(track)
@@ -52,11 +51,11 @@ class MixPlayer(DefaultPlayer):
def remove_user_queue(self, requester: int):
self.queue.remove_user_queue(requester)
def remove_user_track(self, requester: int, pos: int) -> Union[None, AudioTrack]:
def remove_user_track(self, requester: int, pos: int) -> Optional[AudioTrack]:
""" Removes the song at <pos> from the queue of requester """
return self.queue.remove_user_track(requester, pos)
def remove_global_track(self, pos: int):
def remove_global_track(self, pos: int) -> Optional[AudioTrack]:
""" Removes the song at <pos> in the global queue """
return self.queue.remove_global_track(pos)
@@ -64,18 +63,20 @@ class MixPlayer(DefaultPlayer):
""" Randomly reorders the queue of requester """
self.queue.shuffle_user_queue(requester)
def user_queue(self, user: int, dual: bool = False):
if dual:
return self.queue.get_user_queue_with_index(user)
def user_queue(self, user: int) -> List[AudioTrack]:
return self.queue.get_user_queue(user)
def global_queue(self):
def user_queue_with_global_index(self, user: int) -> List[Tuple[AudioTrack, int]]:
return self.queue.get_user_queue_with_index(user)
def global_queue(self) -> List[AudioTrack]:
return self.queue.get_queue()
def get_history(self):
def get_history(self) -> List[AudioTrack]:
return self.queue.history
def queue_duration(self, include_current: bool = False, member: discord.Member = None, end_pos: int = None):
def queue_duration(self, include_current: bool = False,
member: Optional[discord.Member] = None, end_pos: Optional[int] = None):
duration = 0
queue = self.user_queue(member.id) if member else self.queue
@@ -84,14 +85,13 @@ class MixPlayer(DefaultPlayer):
break
duration += int(track.duration)
remaining = self.current.duration - self.position
if include_current:
if self.current:
remaining = self.current.duration - self.position
remaining = self.current.duration - int(self.position)
return lavalink.utils.format_time(duration + remaining)
return lavalink.utils.format_time(duration)
async def play(self, track: AudioTrack = None, start_time: int = 0):
async def play(self, track: Optional[AudioTrack] = None, start_time: int = 0):
self.current = None
self.last_update = 0
@@ -107,10 +107,12 @@ class MixPlayer(DefaultPlayer):
await self.node._dispatch_event(QueueEndEvent(self))
return
else:
# At this point track will not be None, as the queue is not empty
track = self.queue.pop_first()
self.current = track
if track.track is None:
if track is None or track.track is None:
# Ignore, if the queue was empty we would have dispatched the event already
return
await self.node._send(op='play', guildId=str(self.guild_id),
track=track.track, startTime=start_time)
@@ -131,7 +133,7 @@ class MixPlayer(DefaultPlayer):
self.clear_votes()
def update_listeners(self, member, voice_state):
if self.is_connected:
if self.channel_id is not None:
vc = int(self.channel_id)
if voice_state.channel is None or voice_state.channel.id != vc:
self.listeners.discard(member)
@@ -153,7 +155,7 @@ class MixPlayer(DefaultPlayer):
self.voteables[category].add(member)
def remove_member_votes(self, member):
for category, votes in self.voteables.items():
for _, votes in self.voteables.items():
votes.discard(member)
def get_voters(self, category):
@@ -162,7 +164,7 @@ class MixPlayer(DefaultPlayer):
return self.voteables[category]
def clear_votes(self):
for category, votes in self.voteables.items():
for _, votes in self.voteables.items():
votes.clear()
def enable_looping(self, looping):
@@ -179,7 +181,7 @@ class MixPlayer(DefaultPlayer):
if isinstance(event, (TrackStuckEvent, TrackExceptionEvent)) or \
isinstance(event, TrackEndEvent) and event.reason == 'FINISHED':
self.skip_voters.clear()
for category, votes in self.voteables.items():
for _, votes in self.voteables.items():
votes.clear()
await self.play()
@@ -1,33 +1,39 @@
# Discord Packages
import discord
from typing import Optional
from ...mixplayer.player import MixPlayer
from .textpaginator import TextPaginator
class QueuePaginator(TextPaginator):
def __init__(self, localizer, player, color, member: discord.Member = None, include_current: bool = False):
def __init__(self, localizer, player: MixPlayer, color: discord.Color,
member: Optional[discord.Member] = None, include_current: bool = False):
self.localizer = localizer
queue = player.global_queue() if not member else player.user_queue(member.id, dual=True)
# Duration is calculated the same way for both global and user queues
duration = player.queue_duration(member=member, include_current=include_current)
if member:
title = localizer.format_str("{queue.userqueue}", _user=member.display_name, _length=len(queue),
_duration=duration)
member_queue = player.user_queue_with_global_index(member.id)
title = localizer.format_str("{queue.userqueue}",
_user=member.display_name, _length=len(member_queue), _duration=duration)
super().__init__(max_lines=10, **{"color": color, "title": title})
# The user queues also inclue the global position of the tracks
for index, (track, global_pos) in enumerate(member_queue):
queued_track = localizer.format_str("{queue.usertrack}", _index=index+1, _globalindex=global_pos+1,
_title=track.title, _uri=track.uri)
self.add_line(queued_track)
else:
queue = player.global_queue()
title = localizer.format_str("{queue.length}", _length=len(queue), _duration=duration)
super().__init__(max_lines=10, **{"color": color, "title": title})
for index, temp in enumerate(queue):
if member is None:
track = temp
queued_track = localizer.format_str("{queue.globaltrack}", _index=index+1, _title=track.title,
super().__init__(max_lines=10, **{"color": color, "title": title})
for index, track in enumerate(queue, start=1):
queued_track = localizer.format_str("{queue.globaltrack}", _index=index, _title=track.title,
_uri=track.uri, _user_id=track.requester)
else:
track, globpos = temp
queued_track = localizer.format_str("{queue.usertrack}", _index=index+1, _globalindex=globpos+1,
_title=track.title, _uri=track.uri)
self.add_line(queued_track)
self.add_line(queued_track)
self.add_page_indicator(self.localizer, "{queue.pageindicator}")
+3 -3
View File
@@ -5,7 +5,7 @@ import discord
import asyncio
from enum import Flag, auto
from typing import List, Union
from typing import List, Optional
from .paginators import BasePaginator, CantScrollException
@@ -44,7 +44,7 @@ class Scroller:
self.bot = ctx.bot
self.channel = ctx.channel
self.message: Union[discord.Message, None] = None
self.message: Optional[discord.Message] = None
# Initialize the view we'll be using for scrolling
self.view = discord.ui.View(timeout=timeout)
@@ -89,7 +89,7 @@ class Scroller:
raise CantScrollException('Bot cannot send messages.')
async def start_scrolling(self, clear_mode: ClearOn,
message: Union[discord.Message, None] = None,
message: Optional[discord.Message] = None,
start_page: int = 0) -> discord.Message:
self.clear_mode = clear_mode
self.page_number = start_page
+2 -2
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
import discord
from enum import Enum, auto
from typing import Callable, Coroutine, List, Union
from typing import Callable, Coroutine, List, Optional
from .paginators import TextPaginator
from .scroller import ClearOn, Scroller
@@ -117,7 +117,7 @@ class Selector(TextPaginator, Scroller):
self.currently_visible_buttons.append(button)
async def start_scrolling(self, clear_mode: ClearOn = ClearOn.Timeout,
message: Union[discord.Message, None] = None,
message: Optional[discord.Message] = None,
start_page: int = 0):
message = await super().start_scrolling(clear_mode, message, start_page)
return message, self.callback_results