0
# Track Search & Management
1
2
Comprehensive track searching across multiple platforms (YouTube, SoundCloud, YouTube Music) with rich metadata, playlist support, and advanced search capabilities. The track system provides rich objects for individual tracks and playlists with extensive metadata and search functionality.
3
4
## Capabilities
5
6
### Track Search
7
8
Global track searching across supported platforms with automatic source detection and comprehensive result handling.
9
10
```python { .api }
11
# Global search function through Pool
12
async def fetch_tracks(
13
query: str,
14
*,
15
node: Node | None = None
16
) -> list[Playable] | Playlist:
17
"""
18
Search for tracks using the node pool.
19
20
Parameters:
21
- query: Search query (URL, search terms, or platform-specific query)
22
- node: Specific node to use (None for automatic selection)
23
24
Returns:
25
list[Playable] | Playlist: Search results as tracks or playlist
26
27
Raises:
28
LavalinkLoadException: If search fails or no results found
29
"""
30
31
# Track-specific search method
32
class Playable:
33
@classmethod
34
async def search(
35
cls,
36
query: str,
37
*,
38
source: TrackSource | str | None = TrackSource.YouTubeMusic,
39
node: Node | None = None
40
) -> Search:
41
"""
42
Search for tracks with optional source filtering.
43
44
Parameters:
45
- query: Search query string
46
- source: Specific source to search (default: YouTubeMusic)
47
- node: Specific node to use for search
48
49
Returns:
50
Search: List of tracks or playlist (type alias for list[Playable] | Playlist)
51
52
Note:
53
This method applies relevant search prefixes automatically when URLs are not provided.
54
"""
55
56
# Search result type alias
57
Search = list[Playable] | Playlist
58
```
59
60
### Track Objects
61
62
Individual track representation with comprehensive metadata and playback information.
63
64
```python { .api }
65
class Playable:
66
@property
67
def encoded(self) -> str:
68
"""Lavalink encoded track data for internal use."""
69
70
@property
71
def identifier(self) -> str:
72
"""Unique track identifier from the source platform."""
73
74
@property
75
def is_seekable(self) -> bool:
76
"""Whether the track supports seeking to specific positions."""
77
78
@property
79
def author(self) -> str:
80
"""Track author, artist, or uploader name."""
81
82
@property
83
def length(self) -> int:
84
"""Track duration in milliseconds."""
85
86
@property
87
def is_stream(self) -> bool:
88
"""Whether the track is a live stream."""
89
90
@property
91
def position(self) -> int:
92
"""Current playback position in milliseconds."""
93
94
@property
95
def title(self) -> str:
96
"""Track title or name."""
97
98
@property
99
def uri(self) -> str:
100
"""Direct URI to the track source."""
101
102
@property
103
def artwork(self) -> str | None:
104
"""URL to track artwork/thumbnail image."""
105
106
@property
107
def isrc(self) -> str | None:
108
"""International Standard Recording Code."""
109
110
@property
111
def source(self) -> TrackSource:
112
"""Platform source of the track."""
113
114
@property
115
def album(self) -> Album | None:
116
"""Album information if available."""
117
118
@property
119
def artist(self) -> Artist | None:
120
"""Artist information if available."""
121
122
@property
123
def preview_url(self) -> str | None:
124
"""URL to a preview/snippet of the track."""
125
126
@property
127
def is_preview(self) -> bool:
128
"""Whether this track is a preview/snippet."""
129
130
@property
131
def playlist(self) -> PlaylistInfo | None:
132
"""Information about the source playlist."""
133
134
@property
135
def recommended(self) -> bool:
136
"""Whether this track was recommended by AutoPlay."""
137
138
@property
139
def extras(self) -> ExtrasNamespace:
140
"""Additional track metadata and custom attributes."""
141
142
@property
143
def raw_data(self) -> dict[str, Any]:
144
"""Raw track data from Lavalink server."""
145
```
146
147
### Playlist Objects
148
149
Collection of tracks with metadata for handling playlists from various sources.
150
151
```python { .api }
152
class Playlist:
153
def __init__(self, data: dict, tracks: list[Playable]):
154
"""
155
Initialize a playlist with tracks and metadata.
156
157
Parameters:
158
- data: Playlist metadata from Lavalink
159
- tracks: List of tracks in the playlist
160
"""
161
162
@property
163
def name(self) -> str:
164
"""Playlist name or title."""
165
166
@property
167
def tracks(self) -> list[Playable]:
168
"""List of tracks in the playlist."""
169
170
@property
171
def track_count(self) -> int:
172
"""Number of tracks in the playlist."""
173
174
@property
175
def extras(self) -> ExtrasNamespace:
176
"""Additional playlist metadata."""
177
178
def pop(self, index: int = -1) -> Playable:
179
"""
180
Remove and return a track from the playlist.
181
182
Parameters:
183
- index: Index of track to remove (default: last track)
184
185
Returns:
186
Playable: The removed track
187
188
Raises:
189
IndexError: If index is out of range
190
"""
191
192
def track_extras(self, **attrs) -> None:
193
"""
194
Set extra attributes on all tracks in the playlist.
195
196
Parameters:
197
- **attrs: Attributes to set on all tracks
198
"""
199
200
# Container protocol methods
201
def __len__(self) -> int:
202
"""Return the number of tracks in the playlist."""
203
204
def __getitem__(self, index: int | slice) -> Playable | list[Playable]:
205
"""Get track(s) by index or slice."""
206
207
def __iter__(self) -> Iterator[Playable]:
208
"""Iterate over tracks in the playlist."""
209
210
def __bool__(self) -> bool:
211
"""Return True if playlist has tracks."""
212
```
213
214
### Metadata Objects
215
216
Supporting objects for rich track and playlist metadata.
217
218
```python { .api }
219
class Album:
220
"""Album metadata container."""
221
name: str # Album name
222
url: str # Album URL or identifier
223
224
class Artist:
225
"""Artist metadata container."""
226
name: str # Artist name
227
url: str # Artist URL or identifier
228
229
class PlaylistInfo:
230
"""Playlist metadata container."""
231
name: str # Playlist name
232
selected_track: int # Index of selected track in playlist
233
```
234
235
### Track Sources
236
237
Enumeration of supported track sources for platform-specific searching.
238
239
```python { .api }
240
class TrackSource(enum.Enum):
241
"""Enumeration of supported track sources."""
242
YouTube = 0 # YouTube videos
243
YouTubeMusic = 1 # YouTube Music tracks
244
SoundCloud = 2 # SoundCloud tracks
245
```
246
247
## Usage Examples
248
249
### Basic Track Search
250
251
```python
252
import wavelink
253
254
# Search for tracks using various query types
255
async def search_examples():
256
# Search by title and artist
257
tracks = await wavelink.Pool.fetch_tracks("Never Gonna Give You Up Rick Astley")
258
259
# Search with YouTube URL
260
tracks = await wavelink.Pool.fetch_tracks("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
261
262
# Search SoundCloud URL
263
tracks = await wavelink.Pool.fetch_tracks("https://soundcloud.com/artist/track")
264
265
# Search playlist URL
266
result = await wavelink.Pool.fetch_tracks("https://www.youtube.com/playlist?list=...")
267
268
if isinstance(result, wavelink.Playlist):
269
print(f"Found playlist: {result.name} with {len(result)} tracks")
270
for track in result:
271
print(f"- {track.title} by {track.author}")
272
else:
273
print(f"Found {len(result)} tracks")
274
for track in result:
275
print(f"- {track.title} by {track.author} ({track.length // 1000}s)")
276
```
277
278
### Advanced Search with Source Filtering
279
280
```python
281
# Search specific platforms
282
async def platform_search():
283
# Search only YouTube
284
youtube_tracks = await wavelink.Playable.search(
285
"rock music",
286
source=wavelink.TrackSource.YouTube
287
)
288
289
# Search only SoundCloud
290
soundcloud_tracks = await wavelink.Playable.search(
291
"electronic music",
292
source=wavelink.TrackSource.SoundCloud
293
)
294
295
# Search YouTube Music specifically
296
ytmusic_tracks = await wavelink.Playable.search(
297
"classical music",
298
source=wavelink.TrackSource.YouTubeMusic
299
)
300
```
301
302
### Track Information Display
303
304
```python
305
async def display_track_info(track: wavelink.Playable):
306
"""Display comprehensive track information."""
307
print(f"Title: {track.title}")
308
print(f"Artist: {track.author}")
309
print(f"Duration: {track.length // 60000}:{(track.length // 1000) % 60:02d}")
310
print(f"Source: {track.source.name}")
311
print(f"Seekable: {track.is_seekable}")
312
print(f"Stream: {track.is_stream}")
313
print(f"URI: {track.uri}")
314
315
if track.artwork:
316
print(f"Artwork: {track.artwork}")
317
318
if track.album:
319
print(f"Album: {track.album.name}")
320
321
if track.artist:
322
print(f"Artist Info: {track.artist.name}")
323
324
if track.isrc:
325
print(f"ISRC: {track.isrc}")
326
327
# Display any extra metadata
328
if track.extras:
329
print("Extra metadata:", dict(track.extras))
330
```
331
332
### Playlist Handling
333
334
```python
335
async def handle_playlist(query: str):
336
"""Handle playlist search results."""
337
result = await wavelink.Pool.fetch_tracks(query)
338
339
if isinstance(result, wavelink.Playlist):
340
playlist = result
341
print(f"Playlist: {playlist.name}")
342
print(f"Track count: {len(playlist)}")
343
344
# Add custom metadata to all tracks
345
playlist.track_extras(added_by="AutoSearch", priority="high")
346
347
# Get first 5 tracks
348
first_tracks = playlist[:5]
349
350
# Remove and return last track
351
last_track = playlist.pop()
352
353
# Iterate through all tracks
354
for i, track in enumerate(playlist):
355
print(f"{i+1}. {track.title} - {track.author}")
356
357
return playlist
358
else:
359
# Handle individual tracks
360
tracks = result
361
print(f"Found {len(tracks)} individual tracks")
362
return tracks
363
```
364
365
### Search Result Processing
366
367
```python
368
@bot.command()
369
async def search(ctx, *, query: str):
370
"""Search for tracks and display results."""
371
try:
372
result = await wavelink.Pool.fetch_tracks(query)
373
374
if isinstance(result, wavelink.Playlist):
375
# Handle playlist result
376
embed = discord.Embed(
377
title="Playlist Found",
378
description=f"**{result.name}**\n{len(result)} tracks",
379
color=discord.Color.green()
380
)
381
382
# Show first 10 tracks
383
track_list = []
384
for i, track in enumerate(result[:10]):
385
duration = f"{track.length // 60000}:{(track.length // 1000) % 60:02d}"
386
track_list.append(f"{i+1}. {track.title} - {track.author} ({duration})")
387
388
embed.add_field(
389
name="Tracks",
390
value="\n".join(track_list) + ("..." if len(result) > 10 else ""),
391
inline=False
392
)
393
394
else:
395
# Handle track list result
396
if not result:
397
return await ctx.send("No tracks found!")
398
399
embed = discord.Embed(
400
title="Search Results",
401
description=f"Found {len(result)} tracks",
402
color=discord.Color.blue()
403
)
404
405
# Show first 10 results
406
track_list = []
407
for i, track in enumerate(result[:10]):
408
duration = f"{track.length // 60000}:{(track.length // 1000) % 60:02d}"
409
track_list.append(f"{i+1}. {track.title} - {track.author} ({duration})")
410
411
embed.add_field(
412
name="Results",
413
value="\n".join(track_list),
414
inline=False
415
)
416
417
await ctx.send(embed=embed)
418
419
except wavelink.LavalinkLoadException as e:
420
await ctx.send(f"Search failed: {e.error}")
421
```
422
423
### Custom Track Selection
424
425
```python
426
@bot.command()
427
async def choose(ctx, *, query: str):
428
"""Search and let user choose a track."""
429
tracks = await wavelink.Pool.fetch_tracks(query)
430
431
if isinstance(tracks, wavelink.Playlist):
432
tracks = tracks.tracks[:10] # Limit to first 10 from playlist
433
elif isinstance(tracks, list):
434
tracks = tracks[:10] # Limit to first 10 results
435
else:
436
return await ctx.send("No tracks found!")
437
438
if not tracks:
439
return await ctx.send("No tracks found!")
440
441
# Display options
442
description = []
443
for i, track in enumerate(tracks, 1):
444
duration = f"{track.length // 60000}:{(track.length // 1000) % 60:02d}"
445
description.append(f"{i}. {track.title} - {track.author} ({duration})")
446
447
embed = discord.Embed(
448
title="Choose a track",
449
description="\n".join(description),
450
color=discord.Color.blue()
451
)
452
embed.set_footer(text="Type a number to select (1-{})".format(len(tracks)))
453
454
await ctx.send(embed=embed)
455
456
# Wait for user choice
457
def check(message):
458
return (message.author == ctx.author and
459
message.channel == ctx.channel and
460
message.content.isdigit() and
461
1 <= int(message.content) <= len(tracks))
462
463
try:
464
msg = await bot.wait_for('message', check=check, timeout=30.0)
465
choice = int(msg.content) - 1
466
selected_track = tracks[choice]
467
468
# Play the selected track
469
if not ctx.voice_client:
470
player = await ctx.author.voice.channel.connect(cls=wavelink.Player)
471
else:
472
player = ctx.voice_client
473
474
if player.playing:
475
player.queue.put(selected_track)
476
await ctx.send(f"Added to queue: {selected_track.title}")
477
else:
478
await player.play(selected_track)
479
await ctx.send(f"Now playing: {selected_track.title}")
480
481
except asyncio.TimeoutError:
482
await ctx.send("Selection timed out!")
483
```