YuKumo

Autoplay & Utilities

Source-aware autoplay for every music source, plus the 1.7 DX utilities and the 1.8 cross-wrapper DX — offline track building, requester, state getters, repeat aliases, and queue slicing.

Source-Aware Autoplay

Autoplay is no longer YouTube-only. When the queue ends, YuKumo picks a recommendation strategy based on the last track's sourceName:

SourceStrategy
YouTube / YouTube MusicRD radio mix (watch?v=ID&list=RDID)
Spotifysprec:seed_tracks=ID (LavaSrc)
Deezerdzrec:ID
Yandex Musicymrec:ID
SoundCloud<track-url>/recommended
Anything else / fallbackscsearch: / amsearch: / ytsearch: on artist + title

Failed strategies (unsupported prefix on your node) fall through to the next one. Recently played tracks (history + current) are excluded — no repeat loops.

// Per player
player.setAutoplay(true);

// For every created player
const yukumo = new YuKumo({ ..., playerDefaults: { autoplay: true } });

// Custom fetcher still wins over the built-in strategies
player.setAutoplay(true, async (lastTrack) => myRecommender(lastTrack));

// Direct access to the built-in engine
const next = await player.resolveAutoplayTrack(lastTrack);

yukumo.on("autoplayTrackAdded", (guildId, track) => { ... });

waitUntilPlaying

Update embeds/buttons when audio actually starts — no manual trackStart listener:

await player.play(track);
await player.waitUntilPlaying(); // resolves on TrackStartEvent, default timeout 15000ms

Resolves immediately if already playing; rejects on destroy or timeout.

isVoiceReady

if (!player.isVoiceReady) {
  await player.waitForVoiceReady();
}

True when voice credentials are held and the node is connected.

Queue Lock

Serialize multi-step queue edits against concurrent commands:

await player.queue.lock(async () => {
  player.queue.add(tracks);
  player.queue.shuffle();
});

player.queue.isLocked; // true inside a section

Sections run one at a time in call order. Errors propagate to the caller but never break the chain.

queue.unique()

const removed = player.queue.unique();               // dedupe by encoded → info.identifier
player.queue.unique((t) => t.info.title.toLowerCase()); // custom key

Keeps the first occurrence, never removes the playing track, fires onChanged (persistence-aware).

findPlayers

yukumo.findPlayers({ node: "india-01", status: "playing" });
yukumo.findPlayers({ status: ["playing", "paused"], autoplay: true });
yukumo.findPlayers({ filter: (p) => p.queue.size > 10 });
yukumo.players.findOne({ voiceChannelId });

Criteria are AND-combined: node, status (single or array), voiceChannelId, textChannelId, autoplay, stayInVc, playing, filter.

Node Maintenance Mode

Restart or update a Lavalink node without interrupting users:

node.setMaintenance(true); // no NEW players assigned; existing ones keep playing
await node.drain(600_000); // resolves once playerCount hits 0 (timeout 10min here; 0 = forever)
// safe to restart the node now
node.setMaintenance(false);

All built-in selectors skip maintenance nodes. node.maintenance getter reports state.

Temporary Player Data (TTL cache)

player.cache.set("vote", true, 60_000); // auto-expires after 60s
player.cache.get("vote");               // undefined once expired
player.data.set("permanent", value);    // plain Map for permanent data

Lazy expiry — no timers held, nothing keeps the process alive. Cleared on player destroy. TTLCache is exported for standalone use.

broadcast

Apply an operation to all active players (or a filtered subset) concurrently:

await yukumo.broadcast((player) => player.setVolume(80));
await yukumo.broadcast((p) => p.pause(), { status: "playing" });

Runs via Promise.allSettled — one failing player never blocks the rest; results return in player order.

Offline Track Building

Build, encode, and decode tracks entirely on the client — no server round-trip (byte-for-byte compatible with Lavalink v4's native encoding):

import { Track } from "yukumo";

const track = Track.build({ title: "My Local Song", author: "Me" });
await player.play(track); // plays without resolving against a node

const encoded = Track.encode(trackInfo); // → base64 string
const info = Track.decode(encoded);      // → TrackInfo

Requester Support

Pass a requester to search() and it's stamped onto every returned track's userData — cache hits included, without polluting the shared cache:

const result = await yukumo.search({ query: "lofi", requester: { id: "user-1" } });
result.tracks[0]?.userData?.requester; // { id: "user-1" }

// Track.requester picks it up automatically
new Track(result.tracks[0]).requester; // { id: "user-1" }

State Getters

Poru/Riffy-style booleans: isPlaying, isPaused, isConnected, isDestroyed, isAutoplay.

Repeat Aliases

Magmastream/erela.js-style helpers that never clobber the other repeat mode:

player.setTrackRepeat(true); // single-track repeat
player.setQueueRepeat(true); // whole-queue repeat
player.trackRepeat;          // boolean getter
player.queueRepeat = false;  // boolean setter

Queue Slicing

const nextFive = player.get(0, 5); // Poru-compatible slice, current track included

Duration Utils

import { parseDuration } from "yukumo";

parseDuration("3:32");    // 212000
parseDuration("1:02:03"); // 3723000

formatDuration() lives in UIHelpers and powers Track.durationFormatted.

On this page