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

queue-system.mddocs/

0

# Queue System

1

2

Advanced queue management with multiple modes, history tracking, AutoPlay functionality, and thread-safe operations for robust track management. The queue system provides flexible track ordering, looping modes, and automatic track recommendations for continuous playback.

3

4

## Capabilities

5

6

### Queue Management

7

8

Thread-safe queue operations for managing track playback order with support for atomic operations and concurrent access.

9

10

```python { .api }

11

class Queue:

12

def __init__(self, history: Queue | None = None):

13

"""

14

Initialize a new queue.

15

16

Parameters:

17

- history: Optional history queue for storing played tracks

18

"""

19

20

@property

21

def mode(self) -> QueueMode:

22

"""Current queue mode (normal, loop, loop_all)."""

23

24

@mode.setter

25

def mode(self, value: QueueMode) -> None:

26

"""Set the queue mode."""

27

28

@property

29

def history(self) -> Queue | None:

30

"""History queue containing previously played tracks."""

31

32

@property

33

def count(self) -> int:

34

"""Number of tracks currently in the queue."""

35

36

@property

37

def is_empty(self) -> bool:

38

"""Whether the queue is empty."""

39

40

@property

41

def loaded(self) -> Playable | None:

42

"""Pre-loaded next track for smooth transitions."""

43

44

def put(

45

self,

46

item: list[Playable] | Playable | Playlist,

47

*,

48

atomic: bool = True

49

) -> int:

50

"""

51

Add tracks to the queue.

52

53

Parameters:

54

- item: Track(s) or playlist to add

55

- atomic: Whether to add all tracks atomically

56

57

Returns:

58

int: Number of tracks added

59

"""

60

61

async def put_wait(

62

self,

63

item: list[Playable] | Playable | Playlist,

64

*,

65

atomic: bool = True

66

) -> int:

67

"""

68

Add tracks to the queue (async version).

69

70

Parameters:

71

- item: Track(s) or playlist to add

72

- atomic: Whether to add all tracks atomically

73

74

Returns:

75

int: Number of tracks added

76

"""

77

78

def get(self) -> Playable:

79

"""

80

Get the next track from the queue.

81

82

Returns:

83

Playable: Next track to play

84

85

Raises:

86

QueueEmpty: If queue is empty

87

"""

88

89

async def get_wait(self) -> Playable:

90

"""

91

Get the next track from the queue (async, waits if empty).

92

93

Returns:

94

Playable: Next track to play

95

"""

96

97

def get_at(self, index: int) -> Playable:

98

"""

99

Get a track at a specific index without removing it.

100

101

Parameters:

102

- index: Index of the track

103

104

Returns:

105

Playable: Track at the specified index

106

107

Raises:

108

IndexError: If index is out of range

109

"""

110

111

def put_at(self, index: int, value: Playable) -> None:

112

"""

113

Insert a track at a specific index.

114

115

Parameters:

116

- index: Index to insert at

117

- value: Track to insert

118

119

Raises:

120

IndexError: If index is out of range

121

"""

122

```

123

124

### Queue Operations

125

126

Methods for manipulating queue contents including removal, reordering, and searching.

127

128

```python { .api }

129

class Queue:

130

def delete(self, index: int) -> None:

131

"""

132

Remove a track at a specific index.

133

134

Parameters:

135

- index: Index of track to remove

136

137

Raises:

138

IndexError: If index is out of range

139

"""

140

141

def peek(self, index: int = 0) -> Playable:

142

"""

143

View a track without removing it from the queue.

144

145

Parameters:

146

- index: Index of track to peek at (default: next track)

147

148

Returns:

149

Playable: Track at the specified index

150

151

Raises:

152

IndexError: If index is out of range

153

"""

154

155

def swap(self, first: int, second: int) -> None:

156

"""

157

Swap two tracks in the queue.

158

159

Parameters:

160

- first: Index of first track

161

- second: Index of second track

162

163

Raises:

164

IndexError: If either index is out of range

165

"""

166

167

def index(self, item: Playable) -> int:

168

"""

169

Find the index of a track in the queue.

170

171

Parameters:

172

- item: Track to find

173

174

Returns:

175

int: Index of the track

176

177

Raises:

178

ValueError: If track is not in queue

179

"""

180

181

def shuffle(self) -> None:

182

"""Randomly shuffle all tracks in the queue."""

183

184

def clear(self) -> None:

185

"""Remove all tracks from the queue."""

186

187

def copy(self) -> Queue:

188

"""

189

Create a copy of the queue.

190

191

Returns:

192

Queue: New queue with same tracks and settings

193

"""

194

195

def reset(self) -> None:

196

"""Reset the queue to initial state, clearing all tracks and history."""

197

198

def remove(self, item: Playable, /, count: int | None = 1) -> int:

199

"""

200

Remove specific track(s) from the queue.

201

202

Parameters:

203

- item: Track to remove

204

- count: Number of instances to remove (None for all)

205

206

Returns:

207

int: Number of tracks removed

208

"""

209

210

# Container protocol methods

211

def __len__(self) -> int:

212

"""Return the number of tracks in the queue."""

213

214

def __getitem__(self, index: int | slice) -> Playable | list[Playable]:

215

"""Get track(s) by index or slice."""

216

217

def __setitem__(self, index: int, value: Playable) -> None:

218

"""Set track at index."""

219

220

def __delitem__(self, index: int) -> None:

221

"""Delete track at index."""

222

223

def __iter__(self) -> Iterator[Playable]:

224

"""Iterate over tracks in the queue."""

225

226

def __bool__(self) -> bool:

227

"""Return True if queue has tracks."""

228

```

229

230

### Queue Modes

231

232

Enumeration of queue behavior modes for different playback patterns.

233

234

```python { .api }

235

class QueueMode(enum.Enum):

236

"""Queue behavior modes."""

237

normal = 0 # No looping, play through queue once

238

loop = 1 # Loop the current track continuously

239

loop_all = 2 # Loop through entire queue continuously

240

```

241

242

### AutoPlay System

243

244

AutoPlay functionality for continuous music playback with track recommendations.

245

246

```python { .api }

247

class AutoPlayMode(enum.Enum):

248

"""AutoPlay functionality modes."""

249

enabled = 0 # Fully autonomous with track recommendations

250

partial = 1 # Autonomous but no automatic recommendations

251

disabled = 2 # No automatic functionality

252

253

# AutoPlay is integrated into Player class

254

class Player:

255

@property

256

def autoplay(self) -> AutoPlayMode:

257

"""Current AutoPlay mode."""

258

259

@autoplay.setter

260

def autoplay(self, value: AutoPlayMode) -> None:

261

"""Set AutoPlay mode."""

262

263

auto_queue: Queue # Separate queue for AutoPlay recommendations

264

```

265

266

## Usage Examples

267

268

### Basic Queue Operations

269

270

```python

271

import wavelink

272

273

@bot.command()

274

async def queue_example(ctx):

275

"""Demonstrate basic queue operations."""

276

player = ctx.voice_client

277

if not player:

278

return await ctx.send("Not connected to a voice channel!")

279

280

# Add tracks to queue

281

tracks = await wavelink.Pool.fetch_tracks("rock music")

282

if tracks:

283

added = player.queue.put(tracks[:5]) # Add first 5 tracks

284

await ctx.send(f"Added {added} tracks to queue")

285

286

# Check queue status

287

await ctx.send(f"Queue has {player.queue.count} tracks")

288

await ctx.send(f"Queue is empty: {player.queue.is_empty}")

289

290

# Peek at next track

291

if not player.queue.is_empty:

292

next_track = player.queue.peek()

293

await ctx.send(f"Next track: {next_track.title}")

294

295

@bot.command()

296

async def show_queue(ctx, page: int = 1):

297

"""Display the current queue with pagination."""

298

player = ctx.voice_client

299

if not player:

300

return await ctx.send("Not connected to a voice channel!")

301

302

if player.queue.is_empty:

303

return await ctx.send("Queue is empty!")

304

305

# Pagination

306

per_page = 10

307

start_idx = (page - 1) * per_page

308

end_idx = start_idx + per_page

309

310

queue_tracks = list(player.queue)[start_idx:end_idx]

311

312

embed = discord.Embed(

313

title=f"Queue (Page {page})",

314

description=f"Total tracks: {player.queue.count}",

315

color=discord.Color.blue()

316

)

317

318

track_list = []

319

for i, track in enumerate(queue_tracks, start_idx + 1):

320

duration = f"{track.length // 60000}:{(track.length // 1000) % 60:02d}"

321

track_list.append(f"{i}. {track.title} - {track.author} ({duration})")

322

323

embed.add_field(

324

name="Tracks",

325

value="\n".join(track_list) if track_list else "No tracks on this page",

326

inline=False

327

)

328

329

# Add current track info

330

if player.current:

331

embed.add_field(

332

name="Now Playing",

333

value=f"{player.current.title} - {player.current.author}",

334

inline=False

335

)

336

337

await ctx.send(embed=embed)

338

```

339

340

### Queue Manipulation

341

342

```python

343

@bot.command()

344

async def remove_track(ctx, index: int):

345

"""Remove a track from the queue by index."""

346

player = ctx.voice_client

347

if not player:

348

return await ctx.send("Not connected to a voice channel!")

349

350

if player.queue.is_empty:

351

return await ctx.send("Queue is empty!")

352

353

try:

354

# Convert to 0-based index

355

track_index = index - 1

356

357

# Get track info before removing

358

track = player.queue.get_at(track_index)

359

360

# Remove the track

361

player.queue.delete(track_index)

362

363

await ctx.send(f"Removed: {track.title}")

364

365

except IndexError:

366

await ctx.send(f"Invalid index! Queue has {player.queue.count} tracks.")

367

368

@bot.command()

369

async def move_track(ctx, from_pos: int, to_pos: int):

370

"""Move a track from one position to another."""

371

player = ctx.voice_client

372

if not player:

373

return await ctx.send("Not connected to a voice channel!")

374

375

try:

376

# Convert to 0-based indices

377

from_idx = from_pos - 1

378

to_idx = to_pos - 1

379

380

# Get track info

381

track = player.queue.get_at(from_idx)

382

383

# Remove from old position

384

player.queue.delete(from_idx)

385

386

# Adjust target index if it's after the removed track

387

if to_idx > from_idx:

388

to_idx -= 1

389

390

# Insert at new position

391

player.queue.put_at(to_idx, track)

392

393

await ctx.send(f"Moved '{track.title}' from position {from_pos} to {to_pos}")

394

395

except IndexError:

396

await ctx.send("Invalid position! Check queue size.")

397

398

@bot.command()

399

async def swap_tracks(ctx, pos1: int, pos2: int):

400

"""Swap two tracks in the queue."""

401

player = ctx.voice_client

402

if not player:

403

return await ctx.send("Not connected to a voice channel!")

404

405

try:

406

# Convert to 0-based indices

407

idx1 = pos1 - 1

408

idx2 = pos2 - 1

409

410

# Get track info

411

track1 = player.queue.get_at(idx1)

412

track2 = player.queue.get_at(idx2)

413

414

# Swap tracks

415

player.queue.swap(idx1, idx2)

416

417

await ctx.send(f"Swapped '{track1.title}' and '{track2.title}'")

418

419

except IndexError:

420

await ctx.send("Invalid positions! Check queue size.")

421

422

@bot.command()

423

async def shuffle_queue(ctx):

424

"""Shuffle the queue randomly."""

425

player = ctx.voice_client

426

if not player:

427

return await ctx.send("Not connected to a voice channel!")

428

429

if player.queue.count < 2:

430

return await ctx.send("Need at least 2 tracks to shuffle!")

431

432

player.queue.shuffle()

433

await ctx.send(f"Shuffled {player.queue.count} tracks!")

434

435

@bot.command()

436

async def clear_queue(ctx):

437

"""Clear all tracks from the queue."""

438

player = ctx.voice_client

439

if not player:

440

return await ctx.send("Not connected to a voice channel!")

441

442

if player.queue.is_empty:

443

return await ctx.send("Queue is already empty!")

444

445

count = player.queue.count

446

player.queue.clear()

447

await ctx.send(f"Cleared {count} tracks from the queue!")

448

```

449

450

### Queue Modes

451

452

```python

453

@bot.command()

454

async def loop_mode(ctx, mode: str = None):

455

"""Set or check queue loop mode."""

456

player = ctx.voice_client

457

if not player:

458

return await ctx.send("Not connected to a voice channel!")

459

460

if mode is None:

461

current_mode = player.queue.mode.name

462

await ctx.send(f"Current loop mode: {current_mode}")

463

return

464

465

mode_map = {

466

'off': wavelink.QueueMode.normal,

467

'normal': wavelink.QueueMode.normal,

468

'track': wavelink.QueueMode.loop,

469

'loop': wavelink.QueueMode.loop,

470

'all': wavelink.QueueMode.loop_all,

471

'queue': wavelink.QueueMode.loop_all

472

}

473

474

if mode.lower() not in mode_map:

475

return await ctx.send("Valid modes: normal/off, track/loop, all/queue")

476

477

player.queue.mode = mode_map[mode.lower()]

478

await ctx.send(f"Loop mode set to: {player.queue.mode.name}")

479

480

@bot.command()

481

async def autoplay_mode(ctx, mode: str = None):

482

"""Configure AutoPlay mode."""

483

player = ctx.voice_client

484

if not player:

485

return await ctx.send("Not connected to a voice channel!")

486

487

if mode is None:

488

current = player.autoplay.name

489

auto_queue_count = player.auto_queue.count

490

await ctx.send(f"AutoPlay: {current} | Auto queue: {auto_queue_count} tracks")

491

return

492

493

mode_map = {

494

'on': wavelink.AutoPlayMode.enabled,

495

'enabled': wavelink.AutoPlayMode.enabled,

496

'partial': wavelink.AutoPlayMode.partial,

497

'off': wavelink.AutoPlayMode.disabled,

498

'disabled': wavelink.AutoPlayMode.disabled

499

}

500

501

if mode.lower() not in mode_map:

502

return await ctx.send("Valid modes: enabled/on, partial, disabled/off")

503

504

player.autoplay = mode_map[mode.lower()]

505

await ctx.send(f"AutoPlay mode set to: {player.autoplay.name}")

506

```

507

508

### History Management

509

510

```python

511

@bot.command()

512

async def history(ctx, count: int = 10):

513

"""Show recently played tracks."""

514

player = ctx.voice_client

515

if not player:

516

return await ctx.send("Not connected to a voice channel!")

517

518

if not player.queue.history or player.queue.history.is_empty:

519

return await ctx.send("No tracks in history!")

520

521

# Get last N tracks from history

522

history_tracks = list(player.queue.history)[-count:]

523

524

embed = discord.Embed(

525

title="Recently Played",

526

description=f"Last {len(history_tracks)} tracks",

527

color=discord.Color.purple()

528

)

529

530

track_list = []

531

for i, track in enumerate(reversed(history_tracks), 1):

532

track_list.append(f"{i}. {track.title} - {track.author}")

533

534

embed.add_field(

535

name="History",

536

value="\n".join(track_list),

537

inline=False

538

)

539

540

await ctx.send(embed=embed)

541

542

@bot.command()

543

async def replay(ctx, index: int = 1):

544

"""Replay a track from history."""

545

player = ctx.voice_client

546

if not player:

547

return await ctx.send("Not connected to a voice channel!")

548

549

if not player.queue.history or player.queue.history.is_empty:

550

return await ctx.send("No tracks in history!")

551

552

try:

553

# Get track from history (1-based index from most recent)

554

history_list = list(player.queue.history)

555

track = history_list[-(index)]

556

557

# Add to front of queue

558

player.queue.put_at(0, track)

559

await ctx.send(f"Added to front of queue: {track.title}")

560

561

except IndexError:

562

await ctx.send(f"Invalid index! History has {player.queue.history.count} tracks.")

563

```

564

565

### Advanced Queue Features

566

567

```python

568

@bot.command()

569

async def find_track(ctx, *, query: str):

570

"""Find tracks in the queue matching a query."""

571

player = ctx.voice_client

572

if not player:

573

return await ctx.send("Not connected to a voice channel!")

574

575

if player.queue.is_empty:

576

return await ctx.send("Queue is empty!")

577

578

# Search for tracks matching the query

579

matching_tracks = []

580

for i, track in enumerate(player.queue):

581

if (query.lower() in track.title.lower() or

582

query.lower() in track.author.lower()):

583

matching_tracks.append((i + 1, track))

584

585

if not matching_tracks:

586

return await ctx.send(f"No tracks found matching '{query}'")

587

588

embed = discord.Embed(

589

title="Search Results in Queue",

590

description=f"Found {len(matching_tracks)} matching tracks",

591

color=discord.Color.green()

592

)

593

594

result_list = []

595

for pos, track in matching_tracks[:10]: # Limit to 10 results

596

result_list.append(f"{pos}. {track.title} - {track.author}")

597

598

embed.add_field(

599

name="Matches",

600

value="\n".join(result_list),

601

inline=False

602

)

603

604

await ctx.send(embed=embed)

605

606

@bot.command()

607

async def queue_stats(ctx):

608

"""Show detailed queue statistics."""

609

player = ctx.voice_client

610

if not player:

611

return await ctx.send("Not connected to a voice channel!")

612

613

# Calculate total duration

614

total_duration = sum(track.length for track in player.queue)

615

hours = total_duration // 3600000

616

minutes = (total_duration % 3600000) // 60000

617

seconds = (total_duration % 60000) // 1000

618

619

# Count tracks by source

620

source_counts = {}

621

for track in player.queue:

622

source = track.source.name

623

source_counts[source] = source_counts.get(source, 0) + 1

624

625

embed = discord.Embed(

626

title="Queue Statistics",

627

color=discord.Color.blue()

628

)

629

630

embed.add_field(name="Total Tracks", value=player.queue.count, inline=True)

631

embed.add_field(name="Mode", value=player.queue.mode.name, inline=True)

632

embed.add_field(name="AutoPlay", value=player.autoplay.name, inline=True)

633

634

duration_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}"

635

embed.add_field(name="Total Duration", value=duration_str, inline=True)

636

637

if player.queue.history:

638

embed.add_field(name="History Count", value=player.queue.history.count, inline=True)

639

640

embed.add_field(name="Auto Queue", value=player.auto_queue.count, inline=True)

641

642

# Source breakdown

643

if source_counts:

644

source_info = "\n".join(f"{source}: {count}" for source, count in source_counts.items())

645

embed.add_field(name="Sources", value=source_info, inline=False)

646

647

await ctx.send(embed=embed)

648

```