or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

audio-filters.mdevents-exceptions.mdindex.mdnode-management.mdplayer-control.mdqueue-system.mdtrack-search.md

track-search.mddocs/

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

```