YuKumo

API Reference

Comprehensive reference for every public export in YuKumo.

Comprehensive reference for every public export in YuKumo. Supports TypeScript and JavaScript (ESM + CommonJS).

YuKumo

The central orchestrator combining all subsystems.

class YuKumo {
  constructor(options: ManagerOptions);

  // Lifecycle
  init(): Promise<void>;
  destroy(): Promise<void>;

  // Search
  search(query: string | SearchOptions, source?: string): Promise<SearchResult>;
  lavaSearch(query: string, types?: LavaSearchType[], nodeName?: string): Promise<LavaSearchResult>;
  getLyrics(encodedTrack: string, nodeName?: string): Promise<any>;

  // Player management
  createPlayer(options: YuKumoPlayerCreateOptions): Promise<Player>;
  getPlayer(guildId: string): Player | undefined;
  getPlayers(): Player[];
  hasPlayer(guildId: string): boolean;
  destroyPlayer(guildId: string): Promise<boolean>;

  // Playback
  play(guildId: string, track: TrackData): Promise<void>;
  pause(guildId: string): Promise<void>;
  resume(guildId: string): Promise<void>;
  stop(guildId: string): Promise<void>;
  skip(guildId: string): Promise<TrackData | null>;
  setVolume(guildId: string, volume: number): Promise<void>;

  // Voice
  handleVoiceStateUpdate(data: VoiceStateUpdate): Promise<void>;
  handleVoiceServerUpdate(guildId: string, data: VoiceServerUpdate): Promise<void>;

  // Nodes
  getNode(id: string): Node | undefined;
  getNodes(): Node[];

  // Events
  on<E extends EventName>(event: E, callback: EventCallback<E>): this;

  // Subsystems (public for advanced use)
  readonly nodes: NodeManager;
  readonly players: PlayerManager;
  readonly plugins: PluginManager;
  readonly events: EventDispatcher;
  readonly storage: StorageAdapter;
  readonly voice: VoiceStateTracker;
}

ManagerOptions

PropertyTypeDefaultDescription
nodesNodeConfig[]requiredLavalink node configurations
storageAdapterStorageAdapterMemoryStorageCustom storage backend
pluginsPluginDef[][]Plugin instances to register
defaultNodeSelectorNodeSelectorLeastUsedSelectorDefault node selection strategy
send(guildId, payload) => voidRaw Discord gateway payload dispatcher for op: 4 voice state updates
onDisconnect{ destroyPlayer?: boolean; autoReconnect?: boolean }{ destroyPlayer: true }Behavior when bot is disconnected from voice channel
loggerLogger | LogLevelNoopLoggerLogger instance or minimum log level threshold ("debug" | "info" | "warn" | "error" | "silent")
searchCacheSearchCache | CacheOptionsSearchCacheCustom SearchCache instance or cache configuration options (maxSize, ttl)
linksAllowedbooleantrueWhen false, URL queries are rejected with an error load result
linksWhitelist(string | RegExp)[][]When non-empty, URL queries must match at least one entry
linksBlacklist(string | RegExp)[][]URL queries matching any entry are rejected (wins over whitelist)
httpHeadersRecord<string, string>Custom headers for every node's REST requests + WS handshake
queueOptions{ persist?: boolean }{ persist: false }Auto-persist queues to the storage adapter and restore them on createPlayer()
playerDefaults{ maxErrorsPerTime?, minAutoPlayMs?, queueEmptyDestroyMs? }see belowPer-player protection defaults applied on create
playerClassnew (options) => PlayerPlayerCustom Player subclass instantiated by the manager

playerDefaults defaults: maxErrorsPerTime: { threshold: 35000, maxAmount: 3 } (destroy after >3 track errors within 35s; null disables), minAutoPlayMs: 10000 (block autoplay after error-ends of tracks that played under 10s), queueEmptyDestroyMs: 0 (disabled).

NodeConfig

PropertyTypeDefaultDescription
hoststringrequiredLavalink hostname
portnumberrequiredLavalink port
passwordstringrequiredLavalink password
namestringautoNode identifier
securebooleanfalseUse https/wss
resumeTimeoutnumber60Resume timeout (seconds)
resumeKeystringautoResume identifier
maxRetriesnumber5Connection retry limit
retryDelaynumber1000Initial retry delay (ms)
retryDelayMaxnumber30000Maximum retry delay (ms)
connectTimeoutnumber15000WS handshake timeout (ms)
httpHeadersRecord<string, string>Custom headers for this node (overrides manager-level httpHeaders)
enableHeartbeatbooleantrueWS ping/pong dead-connection detection
heartbeatIntervalMsnumber30000Interval between heartbeat pings
heartbeatTimeoutMsnumber10000Pong wait before the socket is declared dead and terminated (auto-reconnects)

Node

Node

PropertyTypeDescription
configNodeConfigConfiguration this node was created with
wsWebSocketClientWebSocket client
restRestClientREST client
stateNodeStatedisconnected | connecting | connected | destroyed
penaltiesPenaltiesCurrent penalty scores
playerCountnumberActive players on this node

NodeManager

MethodReturnsDescription
add(config)NodeRegister a new node
remove(id)booleanRemove and destroy a node
get(id)Node | undefinedGet node by name
getAll()Node[]Get all registered nodes
getConnected()Node[]Get only connected nodes
pick(guildId)Node | nullPick best node via selector
connectAll()Promise<void>Connect all nodes
destroyAll()Promise<void>Destroy all nodes
setSelector(selector)voidReplace selection strategy
size()numberNumber of registered nodes

Node Selectors

SelectorStrategy
LeastUsedSelector(Default) Fewest players
LeastPenaltySelectorLowest penalty (players + CPU + frames)
CpuUsageSelectorLowest Lavalink CPU load
MemoryUsageSelectorLowest memory usage
LowestPingSelectorLowest WebSocket round-trip ping
RegionSelectorRegion-aware with fallback selector
RoundRobinSelectorRound-robin order
RandomSelectorRandom connected node
CustomSelectorCustom function (nodes, guildId) => Node | null
import { RoundRobinSelector, RegionSelector, CustomSelector } from "yukumo";

client.nodes.setSelector(new RoundRobinSelector());

// Region-aware with fallback
client.nodes.setSelector(new RegionSelector("eu", new LeastPenaltySelector()));

// Custom function
client.nodes.setSelector(new CustomSelector((nodes, guildId) => {
  return nodes.find(n => n.state === "connected") ?? null;
}));

Player

Player

PropertyTypeDescription
guildIdstringDiscord guild ID
nodeNodeAssigned Lavalink node
queueQueue<TrackData>Track queue
filtersFilterChainAudio filter chain
eventsEventDispatcherPlayer-local events
dataMap<string, any>Custom data map
statusPlayerStatusidle | playing | paused | destroyed
volumenumberVolume (0–1000)
pausedbooleanWhether paused
currentTrackTrackData | nullCurrent track
voiceStateInternalVoiceState | nullVoice connection state
positionnumberPlayback position (ms) — interpolated between server updates, clamped to track length
ping{ ws: number | null; lavalink: number | null }Node WS ping + per-player Lavalink ping
maxErrorsPerTime{ threshold, maxAmount } | nullSliding-window error-rate protection
minAutoPlayMsnumberMin play time before error-triggered autoplay runs
queueEmptyDestroyMsnumberDestroy this many ms after queue end (0 disables)
MethodReturnsDescription
play(track?, options?)Promise<void>Begin playback. options: { position?, endTime?, noReplace?, paused?, volume? }
playTrack(track, options?)Promise<void>Play specific track (same PlayOptions)
skip()Promise<TTrack | null>Skip — runs the same path as a natural track end (autoplay/repeat/queueEnd all fire)
playPrevious()Promise<TTrack | null>Play previous track from history
pause() / resume()Promise<void>Idempotent pause/resume
stop()Promise<void>Stop playback
setVolume(vol)Promise<void>Change volume (0-1000); remembered while the node is down
seek(position)Promise<void>Seek (ms), clamped to [0, track length]
waitForVoiceReady(timeoutMs?)Promise<void>Wait for Discord voice credentials to be ready before playing (default: 15s)
resync()Promise<void>Re-send full player state & voice credentials to node after reconnect
destroy(reason?)Promise<void>Destroy player; reason (see DestroyReasons) is emitted with playerDestroy
setVoiceChannel(id)Promise<void>Move bot to channel
setTextChannel(id)voidUpdate text channel
search(query, source?)Promise<SearchResult>Shortcut for yukumo.search
setLoop(mode)thisShortcut for queue.setRepeatMode
getLyrics(track?)Promise<any>Fetch lyrics (requires Lyrics plugin)
setSponsorBlock(categories?)Promise<void>Set SponsorBlock auto-skip categories (SponsorBlock plugin)
getSponsorBlock() / deleteSponsorBlock()Promise<string[]> / Promise<void>Read / clear SponsorBlock categories
getCurrentLyrics(skipTrackSource?)Promise<unknown>Lyrics of the current track (LavaLyrics plugin)
subscribeLyrics() / unsubscribeLyrics()Promise<void>Toggle live lyricsLine events (LavaLyrics plugin)
moveNode(nodeId?)Promise<Node>Move to a node — auto-picks the least-loaded connected node when omitted
enableQueuePersistence()voidAuto-save the queue to the storage adapter on every change
restoreQueue()Promise<boolean>Restore a persisted queue
toJSON()PlayerJsonFull serializable player snapshot (queue, filters, voice, playback)

PlayerManager

MethodReturnsDescription
create(options)Promise<Player>Create a player
get(guildId)Player | undefinedGet by guild ID
has(guildId)booleanCheck if exists
destroy(guildId, reason?)Promise<boolean>Destroy a player
getAll()Player[]All players
getByNode(nodeId)Player[]Players on a node
destroyAll(reason?)Promise<void>Destroy all
size()numberActive player count

YuKumoPlayerCreateOptions

PropertyTypeDefaultDescription
guildIdstringrequiredDiscord guild ID
voiceChannelIdstringrequiredVoice channel
textChannelIdstringText channel
selfDeafbooleantrueDeafen on join
selfMutebooleanfalseMute on join
nodeSelectorNodeSelectordefaultPer-player selector override

Queue

Queue<T>

MethodReturnsDescription
enqueue(track, index?)numberAdd track (optionally at index)
dequeue()T | nullRemove and return front
start()voidBegin from front
next()T | nullAdvance to next
previous()T | nullGo back
peek()T | nullView without advancing
clear()voidRemove all
shuffle()voidFisher-Yates shuffle
swap(indexA, indexB)booleanSwap two track positions
skipTo(index)T | nullJump to queue index
remove(index)T | nullRemove at index
removeRange(start, end)T[]Remove range
clearExceptCurrent()voidClear queue except current track
setRepeatMode(mode)void"none" | "track" | "queue"
sortBy(key, order?)thisSort upcoming tracks by "duration" | "title" | "author" or a comparator, "asc" | "desc"
removeTrack(query)T[]Remove by track object / array / predicate (matches identity, then encoded); never removes the playing track
export() / import(state)SerializedQueue / voidSerialize / restore full queue state
toArray()T[]All tracks as array
PropertyTypeDescription
onChanged() => voidHook invoked after every queue mutation — drives queue persistence, usable as a change watcher
PropertyTypeDescription
sizenumberCurrent track count
totalSizenumberIncluding history
currentTrackT | nullCurrent track
repeatModeRepeatModeRepeat setting
const queue = new Queue<TrackData>();
queue.enqueue(track1);
queue.enqueue(track2, 0);
queue.setRepeatMode("queue");
queue.shuffle();
queue.start();

let track;
while ((track = queue.next()) !== null) {
  // process track
}

Track

TrackData

interface TrackData {
  encoded: string;
  info: TrackInfo;
  pluginInfo: Record<string, unknown>;
  userData?: Record<string, unknown>;
}

interface TrackInfo {
  identifier: string;
  isSeekable: boolean;
  author: string;
  length: number;
  isStream: boolean;
  position: number;
  title: string;
  uri: string | null;
  artworkUrl: string | null;
  isrc: string | null;
  sourceName: string;
}

SearchResult

interface SearchResult {
  loadType: "track" | "playlist" | "search" | "empty" | "error";
  tracks: TrackData[];
  playlistInfo?: { name: string; selectedTrack: number };
  exception?: { message: string; severity: "common" | "suspicious" | "fault" };
}

Filters

FilterChain

MethodDescription
add(filter)Add a filter
remove(filter)Remove a filter
clear()Remove all
toPayload()Serialize to API payload
has(filter)Check if exists
getAll()Get all filters

Available Filters

FilterKey Methods
VolumeFiltersetVolume(value)
EqualizerFiltersetBand(index, gain)
KaraokeFiltersetLevel(), setMonoloLevel(), setFilterBand(), setFilterWidth()
TimescaleFiltersetSpeed(), setPitch(), setRate()
TremoloFiltersetFrequency(), setDepth()
VibratoFiltersetFrequency(), setDepth()
RotationFiltersetRotationHz()
DistortionFiltersetSinOffset(), setSinScale(), setCosOffset(), setCosScale(), setTanOffset(), setTanScale(), setOffset(), setScale()
ChannelMixFiltersetLeftToLeft(), setLeftToRight(), setRightToLeft(), setRightToRight()
LowPassFiltersetSmoothing()
import { FilterChain, EqualizerFilter, TimescaleFilter } from "yukumo";

const chain = new FilterChain();
chain
  .add(new EqualizerFilter().setBand(0, 0.4))
  .add(new TimescaleFilter().setSpeed(1.25));

player.filters.add(new EqualizerFilter().setBand(0, 0.5));

Filter Presets

One-line preset methods on FilterChain (also available on player.filters):

PresetDescription
setBassBoost(level)Bass boost EQ preset ("low", "medium", "high", "extreme")
setNightcore(enabled?)Nightcore timescale (speed 1.25, pitch 1.25)
setVaporwave(enabled?)Vaporwave timescale (speed 0.85, pitch 0.8)
setSlowedReverb(enabled?)Slowed + Reverb preset (speed 0.85, pitch 0.85, lowPass filter)
set8D(enabled?) / set3DAudio(enabled?)8D / 3D audio spatial rotation (0.2 Hz panning)
setPitchShift(semitones)Shifts audio pitch by semitones (+/- 12 semitones)
setVoiceIsolation(enabled?)Vocal frequency boost EQ curve
setKaraoke(enabled?)Karaoke vocal removal filter
setPop(enabled?)Pop EQ preset
setSoft(enabled?)Soft EQ preset
setTrebleBass(enabled?)Treble & Bass EQ preset
setRock(enabled?)Rock EQ preset
setClassical(enabled?)Classical EQ preset
setElectronic(enabled?)Electronic EQ preset
setAudioOutput(output)Channel-mix routing: "mono", "stereo" (reset), "left", "right" (presets exported as AudioOutputs)
registerPreset(name, payload)Registers a custom global named preset
applyPreset(name)Applies a registered custom preset
// Apply bass boost
player.filters.setBassBoost("high");

// Enable nightcore
player.filters.setNightcore();

// Disable a preset
player.filters.setNightcore(false);

// Route audio to mono / left / right, back to stereo to reset
player.filters.setAudioOutput("mono");
player.filters.setAudioOutput("stereo");

Voice

VoiceStateTracker

MethodReturnsDescription
handleVoiceStateUpdate(data)Promise<void>Process state update
handleVoiceServerUpdate(guildId, data)Promise<void>Process server update
isReady(guildId)booleanConnection established?
getState(guildId)VoiceState | undefinedGet connection state
getVoiceState(guildId)InternalVoiceState | undefinedGet internal state
getAll()Map<string, VoiceState>All tracked states
remove(guildId)voidRemove state

Voice Events

EventFires When
voiceReadyBoth sessionId and endpoint+token received (50ms debounce)
voiceDisconnectedchannelId becomes null
voiceReconnectingSession ID changes while connected

VoiceStateUpdate

interface VoiceStateUpdate {
  guildId: string;
  sessionId: string;
  channelId: string | null;
  userId?: string;
}

VoiceServerUpdate

interface VoiceServerUpdate {
  token: string;
  endpoint: string | null;
}

Storage

StorageAdapter

interface StorageAdapter {
  get(key: string): Promise<unknown | null>;
  set(key: string, value: unknown): Promise<void>;
  delete(key: string): Promise<boolean>;
  has(key: string): Promise<boolean>;
  clear(): Promise<void>;
}
AdapterDescription
MemoryStorage(Default) In-memory Map. Zero overhead
RedisStorageRedis persistence (requires redis package)
import { RedisStorage } from "yukumo";

const yukumo = new YuKumo({
  nodes: [...],
  storageAdapter: new RedisStorage({ url: "redis://localhost:6379" }),
});

Logging

YuKumo includes a pluggable logging subsystem with level filtering.

Logger Interface

interface Logger {
  debug(message: string, ...args: unknown[]): void;
  info(message: string, ...args: unknown[]): void;
  warn(message: string, ...args: unknown[]): void;
  error(message: string, ...args: unknown[]): void;
}

type LogLevel = "debug" | "info" | "warn" | "error" | "silent";

Loggers & Helper Functions

SymbolDescription
ConsoleLoggerBuilt-in logger outputting formatted logs to standard console
NoopLoggerSilent logger that discards all log messages (default)
levelFilteredLogger(inner: Logger, level: LogLevel)Helper function wrapping a logger to drop messages below the specified threshold
import { YuKumo, ConsoleLogger, levelFilteredLogger } from "yukumo";

const yukumo = new YuKumo({
  nodes: [...],
  logger: levelFilteredLogger(new ConsoleLogger(), "info"),
});

Search & Caching

SearchCache

A high-performance in-memory LRU cache for search results with TTL support.

interface CacheOptions {
  /** Maximum number of entries to keep in cache (default: 100) */
  maxSize?: number;
  /** Time to live in milliseconds (default: 3600000 / 1 hour) */
  ttl?: number;
}

class SearchCache {
  constructor(options?: CacheOptions);
  get<T extends SearchResult | LavaSearchResult>(key: string): T | null;
  set(key: string, data: SearchResult | LavaSearchResult): void;
  clear(): void;
}
import { SearchCache } from "yukumo";

const cache = new SearchCache({ maxSize: 500, ttl: 1800000 }); // 30 mins

Framework Adapters

YuKumo provides first-class gateway adapters that automatically forward voice state and server updates from your Discord library.

AdapterLibrary
DiscordJSAdapterdiscord.js v14
ErisAdapterEris
SeyfertAdapterSeyfert
OceanicAdapterOceanic.js
DiscordenoAdapterDiscordeno
RawGatewayAdapterCustom / raw gateway
DaveyAdapterDavey voice connections

Usage

import { YuKumo, DiscordJSAdapter } from "yukumo";
import { Client } from "discord.js";

const discordClient = new Client({ intents: [...] });
const yukumo = new YuKumo({ nodes: [...] });

// Adapter handles voice state forwarding automatically
const adapter = new DiscordJSAdapter(discordClient, yukumo);

// Connect to voice via the adapter
adapter.sendVoiceStateUpdate(guildId, voiceChannelId);

For custom integrations, use RawGatewayAdapter:

import { RawGatewayAdapter } from "yukumo";

const adapter = new RawGatewayAdapter(yukumo);

// Forward raw gateway payloads manually
adapter.handleRawVoiceStateUpdate(rawPayload);
adapter.handleRawVoiceServerUpdate(rawPayload);

Plugin System

Plugin interface

interface Plugin {
  name: string;
  version: string;
  init?(): void | Promise<void>;
  start?(): void | Promise<void>;
  destroy?(): void | Promise<void>;
}

PluginManager

MethodReturnsDescription
register(plugin)voidRegister plugin
unregister(name)booleanRemove plugin
get(name)Plugin | undefinedGet by name
getAll()Plugin[]All plugins
startAll()Promise<void>Start all
destroyAll()Promise<void>Destroy all
addHook(hook, handler)voidAdd lifecycle hook
removeHook(hook, handler)voidRemove lifecycle hook

Lifecycle Hooks

HookSignatureCancelable
beforeSearch(query, source?) => { query, source? } | nullYes
afterSearch(result) => SearchResult | nullYes
beforeConnect(guildId, channelId) => { guildId, channelId } | nullYes
afterConnect(guildId, channelId) => voidNo
beforePlay(guildId, track) => TrackData | nullYes
afterPlay(guildId, track) => voidNo
beforeDestroy(guildId) => booleanYes (return false)
afterDestroy(guildId) => voidNo
onNodeSelect(guildId, nodes) => string | nullYes

Errors

ErrorWhen
YuKumoErrorBase error class
NodeErrorNode failures
NodeConnectionErrorConnection failures
PlayerErrorPlayer failures
PlayerNotConnectedErrorPlayer not connected
RestErrorHTTP errors (includes status code)
QueueErrorQueue operation errors
QueueFullErrorQueue at capacity
PluginErrorPlugin lifecycle errors
VoiceErrorVoice state errors
LoadErrorTrack loading errors
import { RestError, PlayerNotConnectedError } from "yukumo";

try {
  await yukumo.play("guildId", track);
} catch (error) {
  if (error instanceof RestError) {
    console.error(`HTTP ${error.statusCode}: ${error.message}`);
  }
  if (error instanceof PlayerNotConnectedError) {
    console.error("Player not connected");
  }
}

Events

YuKumo-level

EventPayloadDescription
trackStart(guildId, track)Track started
trackEnd(guildId, track, reason)Track ended
trackStuck(guildId, track, thresholdMs)Track stuck
trackException(guildId, track, exception)Track error
queueEnd(guildId)Queue empty
playerCreate(guildId)Player created
playerDestroy(guildId)Player destroyed
nodeReady(nodeId)Node connected
nodeDisconnected(nodeId, code, reason)Node disconnected
nodeReconnected(nodeId)Node reconnected
nodeError(nodeId, error)Node error
stats(nodeId, stats)Node stats (~60s interval)
voiceReady(guildId)Voice connected
voiceDisconnected(guildId)Voice disconnected
voiceReconnecting(guildId)Voice reconnecting
segmentsLoaded(guildId, segments)SponsorBlock segments loaded for the current track
segmentSkipped(guildId, segment)SponsorBlock segment auto-skipped
chaptersLoaded(guildId, chapters)SponsorBlock chapters loaded
chapterStarted(guildId, chapter)SponsorBlock chapter started
lyricsFound(guildId, lyrics)LavaLyrics: lyrics found
lyricsNotFound(guildId)LavaLyrics: no lyrics found
lyricsLine(guildId, line)LavaLyrics: live lyrics line

playerDestroy now emits (guildId, reason?) — reasons come from the DestroyReasons export:

import { DestroyReasons } from "yukumo";
// QueueEmpty · NodeDestroy · NodeDeleted · LavalinkNoVoice · NodeReconnectFail
// Disconnected · PlayerReconnectFail · ChannelDeleted · DisconnectAllNodes
// TrackErrorMaxTracksErroredPerTime · TrackStuckMaxTracksErroredPerTime
// EmptyVoiceChannel · ManualDestroy

Player-level (player.events)

EventPayload
trackStart(guildId, track)
trackEnd(guildId, track, reason)
trackStuck(guildId, track, thresholdMs)
trackException(guildId, track, exception)
queueEnd(guildId)

On this page