Skip to content

Tracks & Search

Searching

Playable.search() is the universal search method. It handles URLs, plain queries, and all supported sources.

tracks: revvlink.Search = await revvlink.Playable.search(query)

Search is list[Playable] | Playlist. Always check before indexing:

if isinstance(tracks, revvlink.Playlist):
    # It's a full playlist
    await player.queue.put_wait(tracks)
else:
    # It's a list of individual tracks
    track = tracks[0]
    await player.queue.put_wait(track)

Track Sources

Specify a source to override the default search engine:

# Default (YouTube)
tracks = await revvlink.Playable.search("lofi hip hop")

# Explicit sources
tracks = await revvlink.Playable.search("lofi hip hop", source=revvlink.TrackSource.YouTube)
tracks = await revvlink.Playable.search("lofi hip hop", source=revvlink.TrackSource.YouTubeMusic)
tracks = await revvlink.Playable.search("lofi hip hop", source=revvlink.TrackSource.SoundCloud)

# URL-based (auto-detects source)
tracks = await revvlink.Playable.search("https://open.spotify.com/track/...")  # needs LavaSrc
tracks = await revvlink.Playable.search("https://www.youtube.com/watch?v=...")
tracks = await revvlink.Playable.search("https://soundcloud.com/artist/track")

Spotify / Apple Music

Spotify, Apple Music, and Deezer require the LavaSrc Lavalink plugin.

Track Properties

track = tracks[0]

track.title         # str — "Never Gonna Give You Up"
track.author        # str — "Rick Astley"
track.length        # int — duration in milliseconds
track.uri           # str | None — original URL
track.artwork       # str | None — thumbnail/cover image URL
track.source        # str — "youtube", "spotify", etc.
track.isrc          # str | None — ISRC code (if available)
track.recommended   # bool — True if this track was AutoPlay-recommended

track.album.name    # str | None
track.album.url     # str | None

track.playlist.name # str | None — playlist it came from (if any)
track.playlist.url  # str | None

Formatting Duration

def fmt(ms: int) -> str:
    s = ms // 1000
    return f"{s // 60}:{s % 60:02d}"

print(fmt(track.length))  # "3:47"

Playlist

if isinstance(tracks, revvlink.Playlist):
    playlist = tracks
    playlist.name           # str — playlist name
    playlist.url            # str | None
    playlist.selected_track # int — index of auto-selected track (e.g. from URL)
    playlist.artwork        # str | None

    # Iterate tracks
    for track in playlist:
        print(track.title)

    # Bulk-add to queue
    added = await player.queue.put_wait(playlist)
    print(f"Added {added} tracks")

Handling Empty Results

tracks = await revvlink.Playable.search(query)
if not tracks:
    await ctx.send(f"No results found for `{query}`.")
    return

Search Example Command

@bot.command()
async def search(ctx: commands.Context, *, query: str) -> None:
    """Search for tracks and show a numbered list."""
    tracks = await revvlink.Playable.search(query)
    if not tracks or isinstance(tracks, revvlink.Playlist):
        await ctx.send("No track results.")
        return

    results = tracks[:5]
    msg = "\n".join(
        f"`{i+1}.` **{t.title}** — {t.author} `[{t.length // 60000}:{(t.length // 1000) % 60:02d}]`"
        for i, t in enumerate(results)
    )
    await ctx.send(f"Results for `{query}`:\n{msg}\n\nReply with a number to play.")