Refactor library and examples for improved usability and features
Key changes: - Integrated autoplay support into the Player class. - Added new equalizer presets for Pop, Soft, and Light Bass. - Enhanced Queue with move and remove_duplicates functionality. - Updated exception messages and docstrings for better clarity. - Refreshed advanced example with interaction buttons and progress bars.
This commit is contained in:
parent
77d1e3fcbc
commit
590f292275
|
|
@ -301,6 +301,85 @@ class Music(commands.Cog):
|
|||
delete_after=15,
|
||||
)
|
||||
|
||||
@commands.command()
|
||||
async def loop(self, ctx: commands.Context, mode: str = "off"):
|
||||
"""Sets the loop mode: off, track, queue."""
|
||||
player: Player = ctx.voice_client
|
||||
if not player:
|
||||
return
|
||||
|
||||
mode = mode.lower()
|
||||
if mode == "track":
|
||||
player.loop_mode = pomice.LoopMode.TRACK
|
||||
elif mode == "queue":
|
||||
player.loop_mode = pomice.LoopMode.QUEUE
|
||||
else:
|
||||
player.loop_mode = None
|
||||
|
||||
await ctx.send(f"Loop mode set to **{mode}**")
|
||||
|
||||
@commands.command()
|
||||
async def autoplay(self, ctx: commands.Context):
|
||||
"""Toggles autoplay to keep the music going with recommendations when the queue is empty."""
|
||||
player: Player = ctx.voice_client
|
||||
if not player:
|
||||
return
|
||||
|
||||
player.autoplay = not player.autoplay
|
||||
await ctx.send(f"Autoplay is now **{'on' if player.autoplay else 'off'}**")
|
||||
|
||||
@commands.command()
|
||||
async def move(self, ctx: commands.Context, from_index: int, to_index: int):
|
||||
"""Moves a track's position in the queue (e.g., !move 5 1)."""
|
||||
player: Player = ctx.voice_client
|
||||
if not player or player.queue.is_empty:
|
||||
return await ctx.send("The queue is empty.")
|
||||
|
||||
try:
|
||||
player.queue.move(from_index - 1, to_index - 1)
|
||||
await ctx.send(f"Moved track from #{from_index} to #{to_index}.")
|
||||
except IndexError:
|
||||
await ctx.send("Sorry, I couldn't find a track at that position.")
|
||||
|
||||
@commands.command(aliases=["clean"])
|
||||
async def deduplicate(self, ctx: commands.Context):
|
||||
"""Removes any double-posted songs from your queue."""
|
||||
player: Player = ctx.voice_client
|
||||
if not player:
|
||||
return
|
||||
|
||||
removed = player.queue.remove_duplicates()
|
||||
await ctx.send(f"All cleaned up! Removed **{removed}** duplicate tracks.")
|
||||
|
||||
@commands.command()
|
||||
async def filter(self, ctx: commands.Context, preset: str = "off"):
|
||||
"""Apply a sound preset: pop, soft, metal, boost, nightcore, vaporwave, off."""
|
||||
player: Player = ctx.voice_client
|
||||
if not player:
|
||||
return
|
||||
|
||||
preset = preset.lower()
|
||||
await player.reset_filters()
|
||||
|
||||
if preset == "off":
|
||||
return await ctx.send("Filters cleared.")
|
||||
|
||||
presets = {
|
||||
"pop": pomice.Equalizer.pop(),
|
||||
"soft": pomice.Equalizer.soft(),
|
||||
"metal": pomice.Equalizer.metal(),
|
||||
"boost": pomice.Equalizer.boost(),
|
||||
"nightcore": pomice.Timescale.nightcore(),
|
||||
"vaporwave": pomice.Timescale.vaporwave(),
|
||||
"bass": pomice.Equalizer.bass_boost_light()
|
||||
}
|
||||
|
||||
if preset not in presets:
|
||||
return await ctx.send(f"Available presets: {', '.join(presets.keys())}")
|
||||
|
||||
await player.add_filter(presets[preset])
|
||||
await ctx.send(f"Applied the **{preset}** sound preset!")
|
||||
|
||||
@commands.command()
|
||||
async def stop(self, ctx: commands.Context):
|
||||
"""Stop the player and clear all internal states."""
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ class TrackInvalidPosition(PomiceException):
|
|||
|
||||
class TrackLoadError(PomiceException):
|
||||
"""There was an error while loading a track."""
|
||||
|
||||
pass
|
||||
def __init__(self, message: str = "Sorry, I ran into trouble trying to load that track."):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class FilterInvalidArgument(PomiceException):
|
||||
|
|
@ -111,14 +111,14 @@ class QueueException(Exception):
|
|||
|
||||
class QueueFull(QueueException):
|
||||
"""Exception raised when attempting to add to a full Queue."""
|
||||
|
||||
pass
|
||||
def __init__(self, message: str = "Whoops! The queue is completely full right now."):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class QueueEmpty(QueueException):
|
||||
"""Exception raised when attempting to retrieve from an empty Queue."""
|
||||
|
||||
pass
|
||||
def __init__(self, message: str = "It looks like the queue is empty. There's no more music to play!"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class LavalinkVersionIncompatible(PomiceException):
|
||||
|
|
|
|||
|
|
@ -110,10 +110,7 @@ class Equalizer(Filter):
|
|||
|
||||
@classmethod
|
||||
def boost(cls) -> "Equalizer":
|
||||
"""Equalizer preset which boosts the sound of a track,
|
||||
making it sound fun and energetic by increasing the bass
|
||||
and the highs.
|
||||
"""
|
||||
"""A lively preset that boosts both bass and highs, making the music feel more energetic and fun."""
|
||||
|
||||
levels = [
|
||||
(0, -0.075),
|
||||
|
|
@ -134,11 +131,16 @@ class Equalizer(Filter):
|
|||
]
|
||||
return cls(tag="boost", levels=levels)
|
||||
|
||||
@classmethod
|
||||
def bass_boost_light(cls) -> "Equalizer":
|
||||
"""A light touch for people who want a bit more bass without it becoming overwhelming."""
|
||||
levels = [(0, 0.15), (1, 0.1), (2, 0.05)]
|
||||
return cls(tag="bass_boost_light", levels=levels)
|
||||
|
||||
@classmethod
|
||||
def metal(cls) -> "Equalizer":
|
||||
"""Equalizer preset which increases the mids of a track,
|
||||
preferably one of the metal genre, to make it sound
|
||||
more full and concert-like.
|
||||
"""A heavy preset designed to bring out the intensity of metal and rock.
|
||||
It boosts the mids and highs to create a fuller, stage-like sound experience.
|
||||
"""
|
||||
|
||||
levels = [
|
||||
|
|
@ -161,6 +163,30 @@ class Equalizer(Filter):
|
|||
|
||||
return cls(tag="metal", levels=levels)
|
||||
|
||||
@classmethod
|
||||
def pop(cls) -> "Equalizer":
|
||||
"""A balanced preset that enhances vocals and adds a bit of 'pop' to the rhythm.
|
||||
Perfect for mainstream hits.
|
||||
"""
|
||||
levels = [
|
||||
(0, -0.02), (1, -0.01), (2, 0.08), (3, 0.1), (4, 0.15),
|
||||
(5, 0.1), (6, 0.05), (7, 0.0), (8, 0.0), (9, 0.0),
|
||||
(10, 0.05), (11, 0.1), (12, 0.15), (13, 0.1), (14, 0.05)
|
||||
]
|
||||
return cls(tag="pop", levels=levels)
|
||||
|
||||
@classmethod
|
||||
def soft(cls) -> "Equalizer":
|
||||
"""A gentle preset that smooths out harsh frequencies.
|
||||
Ideal for acoustic tracks or when you just want a more relaxed listening experience.
|
||||
"""
|
||||
levels = [
|
||||
(0, 0.0), (1, 0.0), (2, 0.0), (3, -0.05), (4, -0.1),
|
||||
(5, -0.1), (6, -0.05), (7, 0.0), (8, 0.05), (9, 0.1),
|
||||
(10, 0.1), (11, 0.05), (12, 0.0), (13, 0.0), (14, 0.0)
|
||||
]
|
||||
return cls(tag="soft", levels=levels)
|
||||
|
||||
@classmethod
|
||||
def piano(cls) -> "Equalizer":
|
||||
"""Equalizer preset which increases the mids and highs
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ class Player(VoiceProtocol):
|
|||
"_player_endpoint_uri",
|
||||
"queue",
|
||||
"history",
|
||||
"autoplay",
|
||||
)
|
||||
|
||||
def __call__(self, client: Client, channel: VoiceChannel) -> Player:
|
||||
|
|
@ -195,6 +196,7 @@ class Player(VoiceProtocol):
|
|||
|
||||
self.queue: Queue = Queue()
|
||||
self.history: TrackHistory = TrackHistory()
|
||||
self.autoplay: bool = False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
|
|
@ -252,7 +254,7 @@ class Player(VoiceProtocol):
|
|||
|
||||
@property
|
||||
def is_paused(self) -> bool:
|
||||
"""Property which returns whether or not the player has a track which is paused or not."""
|
||||
"""Returns True if the music is currently paused."""
|
||||
return self._is_connected and self._paused
|
||||
|
||||
@property
|
||||
|
|
@ -772,14 +774,25 @@ class Player(VoiceProtocol):
|
|||
await self.seek(self.position)
|
||||
|
||||
async def do_next(self) -> Optional[Track]:
|
||||
"""Automatically plays the next track from the queue.
|
||||
|
||||
"""Automatically picks the next track from the queue and plays it.
|
||||
If the queue is empty and autoplay is on, it will search for recommended tracks.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Optional[Track]
|
||||
The track that is now playing, or None if the queue is empty.
|
||||
The track that's now playing, or None if we've run out of music.
|
||||
"""
|
||||
if self.queue.is_empty:
|
||||
if self.autoplay and self._current:
|
||||
recommendations = await self.get_recommendations(track=self._current)
|
||||
if recommendations:
|
||||
if isinstance(recommendations, Playlist):
|
||||
track = recommendations.tracks[0]
|
||||
else:
|
||||
track = recommendations[0]
|
||||
|
||||
await self.play(track)
|
||||
return track
|
||||
return None
|
||||
|
||||
track = self.queue.get()
|
||||
|
|
|
|||
|
|
@ -310,8 +310,8 @@ class Queue(Iterable[Track]):
|
|||
return new_queue
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Remove all items from the queue."""
|
||||
self._queue.clear()
|
||||
"""Wipes the entire queue clean, removing all tracks."""
|
||||
self._queue = []
|
||||
|
||||
def set_loop_mode(self, mode: LoopMode) -> None:
|
||||
"""
|
||||
|
|
@ -343,8 +343,40 @@ class Queue(Iterable[Track]):
|
|||
self._loop_mode = None
|
||||
|
||||
def shuffle(self) -> None:
|
||||
"""Shuffles the queue."""
|
||||
return random.shuffle(self._queue)
|
||||
"""Mixes up the entire queue in a random order."""
|
||||
random.shuffle(self._queue)
|
||||
|
||||
def move(self, from_index: int, to_index: int) -> None:
|
||||
"""Moves a track from one spot in the queue to another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
from_index: int
|
||||
The current position of the track (0-indexed).
|
||||
to_index: int
|
||||
Where you want to put the track.
|
||||
"""
|
||||
if from_index == to_index:
|
||||
return
|
||||
|
||||
track = self._queue.pop(from_index)
|
||||
self._queue.insert(to_index, track)
|
||||
|
||||
def remove_duplicates(self) -> int:
|
||||
"""Looks through the queue and removes any tracks that appear more than once.
|
||||
Returns the number of duplicate tracks removed.
|
||||
"""
|
||||
initial_count = len(self._queue)
|
||||
seen_ids = set()
|
||||
unique_queue = []
|
||||
|
||||
for track in self._queue:
|
||||
if track.track_id not in seen_ids:
|
||||
unique_queue.append(track)
|
||||
seen_ids.add(track.track_id)
|
||||
|
||||
self._queue = unique_queue
|
||||
return initial_count - len(self._queue)
|
||||
|
||||
def clear_track_filters(self) -> None:
|
||||
"""Clears all filters applied to tracks"""
|
||||
|
|
|
|||
Loading…
Reference in New Issue