0
# Player Control
1
2
Main audio player interface providing playback control, volume management, seeking, and voice channel operations with full discord.py VoiceProtocol integration. The Player class extends discord.VoiceProtocol to seamlessly integrate with Discord's voice system while providing advanced audio playback capabilities.
3
4
## Capabilities
5
6
### Player Creation & Connection
7
8
The Player class integrates with discord.py's voice system to provide audio playback in Discord voice channels.
9
10
```python { .api }
11
class Player(discord.VoiceProtocol):
12
def __init__(self, client: discord.Client, channel: discord.VoiceChannel):
13
"""
14
Initialize a new Player instance.
15
16
Parameters:
17
- client: Discord client instance
18
- channel: Voice channel to connect to
19
"""
20
21
# Predefined queue instances
22
queue: Queue # Standard track queue
23
auto_queue: Queue # AutoPlay queue for recommendations
24
25
async def connect(
26
self,
27
channel: discord.VoiceChannel,
28
*,
29
self_deaf: bool = True,
30
**kwargs
31
) -> None:
32
"""
33
Connect to a Discord voice channel.
34
35
Parameters:
36
- channel: Voice channel to connect to
37
- self_deaf: Whether to self-deafen the bot
38
- **kwargs: Additional connection parameters
39
40
Raises:
41
InvalidChannelStateException: If channel is invalid or no permissions
42
ChannelTimeoutException: If connection times out
43
"""
44
45
async def move_to(self, channel: discord.VoiceChannel) -> None:
46
"""
47
Move to a different voice channel.
48
49
Parameters:
50
- channel: New voice channel to move to
51
"""
52
53
async def disconnect(self, **kwargs) -> None:
54
"""
55
Disconnect from the voice channel.
56
57
Parameters:
58
- **kwargs: Additional disconnection parameters
59
"""
60
```
61
62
### Playback Control
63
64
Core playback functionality including play, pause, seek, skip, and stop operations.
65
66
```python { .api }
67
class Player(discord.VoiceProtocol):
68
async def play(
69
self,
70
track: Playable,
71
*,
72
replace: bool = True,
73
start: int = 0,
74
end: int | None = None,
75
volume: int | None = None,
76
paused: bool | None = None,
77
add_history: bool = True,
78
filters: Filters | None = None,
79
populate: bool = False,
80
max_populate: int = 5
81
) -> Playable:
82
"""
83
Play a track.
84
85
Parameters:
86
- track: The track to play
87
- replace: Whether to replace the current track
88
- start: Start position in milliseconds
89
- end: End position in milliseconds (None for full track)
90
- volume: Playback volume (0-100)
91
- paused: Whether to start paused
92
- add_history: Whether to add current track to history
93
- filters: Filters to apply when playing
94
- populate: Whether to populate AutoPlay queue
95
- max_populate: Maximum tracks to populate for AutoPlay
96
97
Returns:
98
Playable: The track that is now playing
99
"""
100
101
async def pause(self, value: bool) -> None:
102
"""
103
Pause or unpause playback.
104
105
Parameters:
106
- value: True to pause, False to unpause
107
"""
108
109
async def seek(self, position: int = 0) -> None:
110
"""
111
Seek to a specific position in the current track.
112
113
Parameters:
114
- position: Position to seek to in milliseconds
115
116
Raises:
117
ValueError: If track is not seekable
118
"""
119
120
async def stop(self, *, force: bool = True) -> Playable | None:
121
"""
122
Stop the current track.
123
124
Parameters:
125
- force: Whether to force stop immediately
126
127
Returns:
128
Playable | None: The stopped track, if any
129
"""
130
131
async def skip(self, *, force: bool = True) -> Playable | None:
132
"""
133
Skip to the next track in the queue.
134
135
Parameters:
136
- force: Whether to force skip immediately
137
138
Returns:
139
Playable | None: The skipped track, if any
140
"""
141
```
142
143
### Player State & Properties
144
145
Properties for monitoring and controlling player state, volume, and playback information.
146
147
```python { .api }
148
class Player(discord.VoiceProtocol):
149
@property
150
def inactive_channel_tokens(self) -> int | None:
151
"""
152
Token limit for the amount of tracks to play before firing
153
the on_wavelink_inactive_player event when a channel is inactive.
154
155
Returns None if the check has been disabled. A channel is
156
considered inactive when no real members are in the voice channel.
157
Default value is 3. Setting to <= 0 or None disables the check.
158
"""
159
160
@inactive_channel_tokens.setter
161
def inactive_channel_tokens(self, value: int | None) -> None:
162
"""Set the inactive channel token limit."""
163
164
@property
165
def inactive_timeout(self) -> int | None:
166
"""
167
Time in seconds to wait before dispatching the
168
on_wavelink_inactive_player event for inactive players.
169
170
Returns None if no timeout is set. An inactive player is one
171
that has not been playing anything for the specified seconds.
172
The countdown starts when a track ends and cancels when a track starts.
173
"""
174
175
@inactive_timeout.setter
176
def inactive_timeout(self, value: int | None) -> None:
177
"""Set the inactive timeout in seconds."""
178
179
@property
180
def autoplay(self) -> AutoPlayMode:
181
"""Current AutoPlay mode setting."""
182
183
@autoplay.setter
184
def autoplay(self, value: AutoPlayMode) -> None:
185
"""Set the AutoPlay mode."""
186
187
@property
188
def node(self) -> Node:
189
"""The Lavalink node this player is connected to."""
190
191
@property
192
def guild(self) -> discord.Guild | None:
193
"""The Discord guild this player belongs to."""
194
195
@property
196
def connected(self) -> bool:
197
"""Whether the player is connected to a voice channel."""
198
199
@property
200
def current(self) -> Playable | None:
201
"""The currently playing track."""
202
203
@property
204
def volume(self) -> int:
205
"""Current player volume (0-100)."""
206
207
@property
208
def filters(self) -> Filters:
209
"""Currently applied audio filters."""
210
211
@property
212
def paused(self) -> bool:
213
"""Whether playback is currently paused."""
214
215
@property
216
def ping(self) -> int:
217
"""WebSocket ping to the Lavalink server in milliseconds."""
218
219
@property
220
def playing(self) -> bool:
221
"""Whether the player is currently playing a track."""
222
223
@property
224
def position(self) -> int:
225
"""Current playback position in milliseconds."""
226
```
227
228
### Volume & Filter Control
229
230
Methods for controlling audio volume and applying audio filters to enhance playback.
231
232
```python { .api }
233
class Player(discord.VoiceProtocol):
234
async def set_volume(self, value: int = 100) -> None:
235
"""
236
Set the player volume.
237
238
Parameters:
239
- value: Volume level (0-100)
240
241
Raises:
242
ValueError: If volume is outside valid range
243
"""
244
245
async def set_filters(
246
self,
247
filters: Filters | None = None,
248
*,
249
seek: bool = False
250
) -> None:
251
"""
252
Apply audio filters to the player.
253
254
Parameters:
255
- filters: Filters object to apply (None to reset all filters)
256
- seek: Whether to seek to current position after applying filters
257
"""
258
```
259
260
### Event Handlers
261
262
Event handler methods that can be overridden to respond to player events.
263
264
```python { .api }
265
class Player(discord.VoiceProtocol):
266
async def on_voice_state_update(self, data) -> None:
267
"""
268
Handle voice state updates from Discord.
269
270
Parameters:
271
- data: Voice state update data
272
"""
273
274
async def on_voice_server_update(self, data) -> None:
275
"""
276
Handle voice server updates from Discord.
277
278
Parameters:
279
- data: Voice server update data
280
"""
281
```
282
283
## Usage Examples
284
285
### Basic Player Setup
286
287
```python
288
import discord
289
import wavelink
290
from discord.ext import commands
291
292
bot = commands.Bot(command_prefix='!', intents=discord.Intents.all())
293
294
@bot.command()
295
async def join(ctx):
296
"""Join the user's voice channel."""
297
if not ctx.author.voice:
298
return await ctx.send("You're not in a voice channel!")
299
300
channel = ctx.author.voice.channel
301
player = await channel.connect(cls=wavelink.Player)
302
await ctx.send(f"Connected to {channel.name}")
303
304
@bot.command()
305
async def leave(ctx):
306
"""Leave the voice channel."""
307
player = ctx.voice_client
308
if player:
309
await player.disconnect()
310
await ctx.send("Disconnected!")
311
```
312
313
### Playback Control
314
315
```python
316
@bot.command()
317
async def play(ctx, *, query: str):
318
"""Play a track or add to queue."""
319
if not ctx.voice_client:
320
player = await ctx.author.voice.channel.connect(cls=wavelink.Player)
321
else:
322
player = ctx.voice_client
323
324
# Search for tracks
325
tracks = await wavelink.Pool.fetch_tracks(query)
326
if not tracks:
327
return await ctx.send("No tracks found!")
328
329
track = tracks[0] if isinstance(tracks, list) else tracks.tracks[0]
330
331
if player.playing:
332
# Add to queue if already playing
333
player.queue.put(track)
334
await ctx.send(f"Added to queue: {track.title}")
335
else:
336
# Start playing immediately
337
await player.play(track)
338
await ctx.send(f"Now playing: {track.title}")
339
340
@bot.command()
341
async def pause(ctx):
342
"""Pause or unpause playback."""
343
player = ctx.voice_client
344
if not player:
345
return await ctx.send("Not connected to a voice channel!")
346
347
await player.pause(not player.paused)
348
action = "Paused" if player.paused else "Resumed"
349
await ctx.send(f"{action} playback!")
350
351
@bot.command()
352
async def skip(ctx):
353
"""Skip the current track."""
354
player = ctx.voice_client
355
if not player or not player.playing:
356
return await ctx.send("Nothing is playing!")
357
358
skipped_track = await player.skip()
359
await ctx.send(f"Skipped: {skipped_track.title}")
360
361
@bot.command()
362
async def seek(ctx, seconds: int):
363
"""Seek to a position in the current track."""
364
player = ctx.voice_client
365
if not player or not player.current:
366
return await ctx.send("Nothing is playing!")
367
368
if not player.current.is_seekable:
369
return await ctx.send("This track is not seekable!")
370
371
position = seconds * 1000 # Convert to milliseconds
372
await player.seek(position)
373
await ctx.send(f"Seeked to {seconds} seconds")
374
```
375
376
### Volume Control
377
378
```python
379
@bot.command()
380
async def volume(ctx, level: int = None):
381
"""Get or set the player volume."""
382
player = ctx.voice_client
383
if not player:
384
return await ctx.send("Not connected to a voice channel!")
385
386
if level is None:
387
await ctx.send(f"Current volume: {player.volume}%")
388
else:
389
if not 0 <= level <= 100:
390
return await ctx.send("Volume must be between 0 and 100!")
391
392
await player.set_volume(level)
393
await ctx.send(f"Volume set to {level}%")
394
```
395
396
### AutoPlay Configuration
397
398
```python
399
@bot.command()
400
async def autoplay(ctx, mode: str = None):
401
"""Configure AutoPlay mode."""
402
player = ctx.voice_client
403
if not player:
404
return await ctx.send("Not connected to a voice channel!")
405
406
if mode is None:
407
current = player.autoplay.name
408
await ctx.send(f"AutoPlay mode: {current}")
409
return
410
411
mode_map = {
412
'enabled': wavelink.AutoPlayMode.enabled,
413
'partial': wavelink.AutoPlayMode.partial,
414
'disabled': wavelink.AutoPlayMode.disabled
415
}
416
417
if mode.lower() not in mode_map:
418
return await ctx.send("Valid modes: enabled, partial, disabled")
419
420
player.autoplay = mode_map[mode.lower()]
421
await ctx.send(f"AutoPlay mode set to: {mode}")
422
```
423
424
### Player Information
425
426
```python
427
@bot.command()
428
async def now_playing(ctx):
429
"""Show information about the current track."""
430
player = ctx.voice_client
431
if not player or not player.current:
432
return await ctx.send("Nothing is playing!")
433
434
track = player.current
435
embed = discord.Embed(
436
title="Now Playing",
437
description=f"**{track.title}**\nby {track.author}",
438
color=discord.Color.blue()
439
)
440
441
embed.add_field(
442
name="Duration",
443
value=f"{track.length // 60000}:{(track.length // 1000) % 60:02d}",
444
inline=True
445
)
446
embed.add_field(
447
name="Position",
448
value=f"{player.position // 60000}:{(player.position // 1000) % 60:02d}",
449
inline=True
450
)
451
embed.add_field(name="Volume", value=f"{player.volume}%", inline=True)
452
embed.add_field(name="Source", value=track.source.name, inline=True)
453
embed.add_field(name="Paused", value=player.paused, inline=True)
454
embed.add_field(name="Queue Size", value=player.queue.count, inline=True)
455
456
if track.artwork:
457
embed.set_thumbnail(url=track.artwork)
458
459
await ctx.send(embed=embed)
460
```