Skip to main content

Complete Lavalink replacement for Discord.py — local music player with built-in JioSaavn and Gaana support

Project description

Voicecord.py

MIT License Python 3.10+ discord.py

Complete Lavalink replacement for Discord.py. Local music player with built-in JioSaavn and Gaana support, queue management, autoplay, filters, and plugin system. No external server needed.

No Lavalink. No Java. No separate server. Just Python + FFmpeg.


Installation

pip install voicecord

Requires: FFmpeg installed and in PATH.


Quick Start

import discord
from voicecord import Player, Pool, LoopMode

bot = discord.Bot()

@bot.slash_command()
async def play(ctx, query: str):
    if not ctx.author.voice:
        return await ctx.respond("Join a voice channel first.")

    player = await Pool.connect(ctx.author.voice.channel, cls_type=Player)
    tracks = await player.search(query)
    track = tracks[0]
    track.requester = ctx.author

    player.queue.add(track)
    await ctx.respond(f"Added **{track.title}** to queue.")

    if not player.playing:
        await player.play(player.queue.next())

@bot.slash_command()
async def skip(ctx):
    player = Pool.get_player(ctx.guild)
    if player:
        await player.skip()
        await ctx.respond("Skipped.")

@bot.slash_command()
async def queue(ctx):
    player = Pool.get_player(ctx.guild)
    if not player or player.queue.is_empty:
        return await ctx.respond("Queue is empty.")
    tracks = "\n".join(f"{i+1}. {t.title}" for i, t in enumerate(player.queue))
    await ctx.respond(f"**Queue:**\n{tracks}")

@bot.slash_command()
async def loop(ctx, mode: str):
    player = Pool.get_player(ctx.guild)
    if not player:
        return
    modes = {"off": LoopMode.NONE, "track": LoopMode.TRACK, "queue": LoopMode.QUEUE}
    player.loop = modes.get(mode, LoopMode.NONE)
    await ctx.respond(f"Loop: **{mode}**")

@bot.event
async def on_voicecord_track_start(player, track):
    print(f"Now playing: {track.title} by {track.author}")

@bot.event
async def on_voicecord_queue_end(player):
    await player.disconnect()

bot.run("TOKEN")

Player

The Player extends discord.VoiceClient — it IS the voice connection.

Playback

Method Description
await player.play(track) Play a track
await player.pause() Pause playback
await player.resume() Resume playback
await player.skip() Skip to next track
await player.previous() Play previous from history
await player.stop() Stop and clear queue
await player.seek(position_ms) Seek to position
await player.set_volume(0-200) Set volume
await player.disconnect() Leave voice channel

Search

Method Description
await player.search(query) Search default source (JioSaavn)
await player.search(query, source="gaana") Search specific source
await player.fetch_tracks(query) Auto-detect URLs or search

Properties

Property Type Description
player.current Track Currently playing track
player.queue Queue Track queue
player.state PlayerState IDLE/PLAYING/PAUSED/STOPPED
player.volume int Current volume (0-200)
player.position float Playback position in ms
player.loop LoopMode NONE/TRACK/QUEUE
player.playing bool Is playing or paused
player.connected bool Is connected to voice
player.autoplay AutoPlay Autoplay manager
player.filters FilterChain Active filters

Queue

Method Description
queue.add(track) Add to end
queue.add_many(tracks) Add multiple
queue.add_at(index, track) Insert at position
queue.remove(index) Remove by index
queue.clear() Clear all
queue.shuffle() Shuffle queue
queue.reverse() Reverse order
queue.move(from, to) Move track
queue.swap(i, j) Swap two tracks
queue.skip_to(index) Jump to index
queue.next() Get next (respects loop)
queue.previous() Go back in history
queue.peek() See next without popping
Property Type Description
queue.current Track Current track
queue.upcoming List[Track] Upcoming tracks
queue.history List[Track] Previously played
queue.count int Queue length
queue.is_empty bool Is empty
queue.duration int Total duration (ms)
queue.loop LoopMode Loop mode

Sources

JioSaavn is the default source. All sources implement BaseSource.

Built-in Sources

Source Search Albums Playlists Artists Recommendations Stream
jiosaavn (default) 320kbps AAC
gaana HLS
url Direct

Switch Source

player.set_default_source("gaana")
tracks = await player.search("query", source="jiosaavn")

Custom Source

from voicecord import BaseSource, Track

class SpotifySource(BaseSource):
    name = "spotify"
    supports_search = True

    async def search(self, query, limit=10):
        # your implementation
        return [Track(title="...", author="...", source=self.name)]

    async def get_track(self, identifier):
        return Track(...)

    async def get_stream_url(self, track):
        return "https://..."

player.register_source(SpotifySource())

Filters

from voicecord import BassBoost, Nightcore, Slowed, Volume, Karaoke, EightD, Tremolo

await player.set_filter(BassBoost(level=10))
await player.set_filter(Nightcore(speed=1.3))
await player.set_filter(BassBoost(5), Volume(0.8))  # combine
await player.clear_filters()
Filter Parameter Range
Volume(level) Volume multiplier 0.0 - 2.0
BassBoost(level) Bass gain dB 0 - 20
Nightcore(speed) Speed multiplier 1.0 - 2.0
Slowed(speed) Speed multiplier 0.5 - 1.0
Tremolo(freq, depth) Wobble effect freq: 0.1-20, depth: 0-1
Karaoke() Remove vocals
EightD(speed) Panning effect 0.01 - 1.0

Autoplay

player.autoplay.enabled = True

When the queue is empty and autoplay is enabled, the player automatically fetches recommendations from JioSaavn based on the last played track and continues playing. Avoids repeats via internal history.


Loop Modes

from voicecord import LoopMode

player.loop = LoopMode.NONE     # no repeat
player.loop = LoopMode.TRACK    # repeat current track
player.loop = LoopMode.QUEUE    # repeat entire queue

Events

@bot.event
async def on_voicecord_track_start(player, track):
    print(f"Playing: {track.title}")

@bot.event
async def on_voicecord_track_end(player, track, reason):
    print(f"Finished: {track.title}")

@bot.event
async def on_voicecord_queue_end(player):
    await player.disconnect()

@bot.event
async def on_voicecord_autoplay(player, track):
    print(f"Autoplay: {track.title}")

@bot.event
async def on_voicecord_track_exception(player, track, error):
    print(f"Error: {error}")
Event Arguments
on_voicecord_track_start player, track
on_voicecord_track_end player, track, reason
on_voicecord_track_exception player, track, error
on_voicecord_queue_end player
on_voicecord_autoplay player, track
on_voicecord_player_paused player
on_voicecord_player_resumed player
on_voicecord_player_disconnect player

Plugin System

from voicecord import BasePlugin

class LogPlugin(BasePlugin):
    name = "logger"

    async def on_track_start(self, player, track):
        print(f"[LOG] Playing: {track}")

    async def on_track_end(self, player, track):
        print(f"[LOG] Ended: {track}")

    async def on_queue_end(self, player):
        print(f"[LOG] Queue empty in {player.guild.name}")

player.plugins.load(LogPlugin())

Pool

from voicecord import Pool, Player

player = await Pool.connect(channel, cls_type=Player)
player = Pool.get_player(guild)
await Pool.disconnect(guild)
await Pool.disconnect_all()

Track

Field Type
title str
author str
identifier str
uri str
length int (ms)
artwork str
source str
stream_url str
is_seekable bool
is_stream bool
album_name str
isrc str
requester Any
duration int (seconds)
duration_ms int (ms)

Comparison with Lavalink

Feature Voicecord.py Wavelink/Pomice
Server Required ❌ No ✅ Lavalink (Java)
Setup pip install voicecord Install Java + Lavalink + config
JioSaavn Built-in ✅ (default) ❌ Needs plugin
Gaana Built-in
Custom Sources ✅ Python class ❌ Java plugin
Audio Filters ✅ FFmpeg ✅ Lavalink
Autoplay ✅ Built-in ❌ Manual
Queue ✅ Built-in ✅ Built-in
Plugin System ✅ Python ❌ Java
Latency Local FFmpeg Network to server

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

voicecord_py-1.0.0.tar.gz (18.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

voicecord_py-1.0.0-py3-none-any.whl (20.8 kB view details)

Uploaded Python 3

File details

Details for the file voicecord_py-1.0.0.tar.gz.

File metadata

  • Download URL: voicecord_py-1.0.0.tar.gz
  • Upload date:
  • Size: 18.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for voicecord_py-1.0.0.tar.gz
Algorithm Hash digest
SHA256 446e335405d598416a4670a79878a9abec4c5dc47e9f5514e0198d0e9f9e67a5
MD5 a46d741623d1aee48c748095a2c69aae
BLAKE2b-256 4a3228c917084c0036f56f63eeefd6aa9e8ec3b8d5aca9f1c5efa8bd7a750d46

See more details on using hashes here.

File details

Details for the file voicecord_py-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: voicecord_py-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 20.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for voicecord_py-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1993d5dba8be976186f569105332a8049b88b735f8ee0e9079a40e04539e7a6a
MD5 d9e0f7fe3f14a6c863714c4f0a5a976d
BLAKE2b-256 6bdf246d0091a30037d05b6cc9d3d6c23ac6cbeba95aeae5b2d1dc7d72ee8d4d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page